//the REAL game board
/*
    GameBoard extends JPanel implements MouseListener, MouseMotionListener
        :Main board component of the game that displays the boards and the pieces.
        :This also allows interaction with the board and pieces.
        :It is part of the GameView.
        Attributes
            int currentBoard
            int currentView
            ArrayList<Point> currentMoves
            ArrayList<Boolean> currentJumps
            ArrayList<BufferedImage> boardImages
            ArrayList<String> pieceNames
            ArrayList<BufferedImage> pieceImages
            ArrayList<String> boardPieces
            String animationPiece
            int animationStep
        Methods
            mouseDragged(MouseEvent)
            mouseMoved(MouseEvent)
            mouseClicked(MouseEvent)
            mouseEntered(MouseEvent)
            mouseExited(MouseEvent)
            mousePressed(MouseEvent)
            mouseReleased(MouseEvent)
            getNextBoard()
                :calls UI Controller -> getBoard(int) with currentBoard + 1
            getPreviousBoard()
                :calls UI Controller -> getBoard(int) with currentBoard - 1
            changeBoard(int)
                :calls UI Controller -> getBoard(int) with specified integer
            changeView(int)
            paintComponent(Graphics)
                :customized drawing of this component
                :draw the board depending on the currentView, currentBoard and boardImages
                :if animationStep in on position then call animatePiece with animationPiece data
                :draw pieces using boardPieces, pieceNames and pieceImages
            ArrayList<Point> animatePiece(String)
                :used to get transformation Points
            startAnimation(String)
                :set data of animationPiece using supplied String
                :set animationStep to initial position
            stopAnimation()
                :set animationStep to off position
            boolean discard()
                :show a question panel asking if the user is ok with discarding the current game
                :if the user agrees, discard all game information and set the state back to uninitialized
*/

//Do not pay too much attention to the above design, I will likely change it to suit the real specifications

//This jpanel uses states so that the paintComponent method 
//  knows when it should draw different things
//The ActionListener implementation is used to handle Timer events 
//  used to tell this component to redraw when needed
//  such a case where we would need to keep redrawing would be
//  if the user is placing a piece either at board setup or during a move
//  or if we need to animate something

import javax.swing.JPanel;
import javax.swing.JOptionPane;
import javax.swing.Timer;
import javax.swing.JLabel;
import javax.swing.JComboBox;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.IIOImage;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseEvent;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.Polygon;
import java.awt.Point;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.Image;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.Font;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.Iterator;
import java.io.IOException;
import java.io.File;

public class GameBoard extends JPanel 
	implements MouseListener, MouseMotionListener, ActionListener {
	
	//declare any variables we need to use later
	//state related vars
		//board state can be UNSTARTED, SETUP, REQUEST, PLACEMENT, PLAY or REPLAY
		private int boardstate;
		//view state can be SIDE_VIEW or BACK_VIEW
		private int viewstate;
		//click flag to help block parallel click event threads
		private boolean hasclicked;
		
	//game data related vars
		//the size of the board that we will be using
		private Dimension boardsize;
		//the coin side selected by the first player
		// should be 0 for heads or 1 for tails
		private int coinside;
		//flag indicating if piece placement should be done automatically with randomization
		private boolean randomize;
		//the number of boards in this game
		private int boardcount;
		//the number of players in this game
		private int playercount;
		//the board we want the user to actually see
		private int currentboard;
		//the x,y coord of the board z we are looking at
		private GameBoard.Point3D currentposition;
		//the x,y coord of the board z we were looking at
		// used when moving a piece to know where it came from
		private GameBoard.Point3D previousposition;
		//the index of the playerlist for the player we are looking at
		private int currentplayer;
		//the index of the playerlist for the player that gets the first turn
		private int firstplayer;
		//a list of player names
		private ArrayList<String> playerlist;
		//a list of booleans representing if the corresponding player index can still place pieces
		// used in PLACEMENT mode to help decide if we should be done placing
		private ArrayList<Boolean> canplace;
		//a list of positions for pieces, related to piecetypes
		//private ArrayList<GameBoard.Point3D> piecepositions;
		//a list of types for pieces, outer=xcoord inner1=ycoord inner2=board
		private ArrayList<ArrayList<ArrayList<ArrayList<String>>>> piecetypes;
		//a list of owners for pieces, outer=xcoord inner1=ycoord inner2=board
		private ArrayList<ArrayList<ArrayList<ArrayList<Integer>>>> pieceowners;
		//the type of the current piece we are looking at
		private String currentpiece;
		//a list of positions that represent the current piece's current move
		private ArrayList<GameBoard.Point3D> currentmove;
		//a list of movable piece locations used to store what pieces the user can legally select for moving
		private ArrayList<GameBoard.Point3D> movablelocations;
		//a list of movable piece types used to store what pieces the user can legally select for moving
		private ArrayList<String> movabletypes;
		//a list of locations the current piece can legally move to
		private ArrayList<GameBoard.Point3D> availablemoves;
		
	//game action related vars
		//the last recorded mouse position
		private Point mousepos;
		//a flag to indicate the user is moving a piece around the board
		// and as such, we should draw the piece following the mouse
		private boolean movingpiece;
		
	//draw scheduling related vars
		//an object used for scheduling timed events (animation or popups)
		private Timer boardtimer;
		//repaints per second 
		private int fps;
		
	//move hints related vars
		//flag for highlighting pieces to hint at moves
		private boolean showmoves;
		//list of positions that represents moves available
		private ArrayList<GameBoard.Point3D> movehints;
		
	//vars used for replays
		//a set of board data specifically for replays
		//a list of types for pieces, outer=xcoord inner1=ycoord inner2=board
		private ArrayList<ArrayList<ArrayList<ArrayList<String>>>> replaytypes;
		//a list of owners for pieces, outer=xcoord inner1=ycoord inner2=board
		private ArrayList<ArrayList<ArrayList<ArrayList<Integer>>>> replayowners;
		//a list of turns containing a list of moves to replay
		private ArrayList<ArrayList<ArrayList<String>>> replaymoves;
		//current position in our list of moves
		private int replaymov;
		//current position in our list of turns
		private int replaymovepos;
		//current owner for a move
		private int replayowner;
		//replay action list to hold information specific
		// to what actions are required to properly display a replay
		private ArrayList<String> replayactions;
		//current position in the replay action list that should be drawn
		private int replayactpos;
		//frame during an animation that should be drawn
		private int animationstep;
		//the total frames to be drawn for the current animation
		private int animsteps;
		//positions (on the screen) of the frame animations
		private ArrayList<Point> animpos;
		//dimensions (size) of the frame animations
		private ArrayList<Dimension> animdims;
		//index id of the image being animated
		private int animindex;
		//current animation frame dimension
		private Dimension stepdim;
		//current animation frame position
		private Point steppos;
		
	//vars used for imaging
		//our preferred size for this component
		private Dimension ourpreferredsize;
		//image locations
		//list of unique names that correspond to properties
		// of the images found in the same index position in the
		// other image property arrays
		private ArrayList<String> imagenames;
		//filename location of the image
		private ArrayList<String> imagelocs;
		//height and width of the image during default view
		private ArrayList<Dimension> imagedims;
		//position of image relative to the component
		// or specific transformation for non-fixed images
		private ArrayList<Point> imagepos;
		//objects references to the saved image instance data
		private ArrayList<BufferedImage> imagerefs;
		//object references to lists of image frames used for animated images
		private ArrayList<ArrayList<BufferedImage>> animaterefs;
		//the current image of the animated images
		private ArrayList<Integer> currentframe;
		//objects references to the scaled image instance data
		private ArrayList<Image> scaleimagerefs;
		//the scale used for the scaled image instance data
		private ArrayList<Dimension> previousscale;
		//image quality
		private int scalequality;
		//specific board rectangle drawing area relative to component preffered size
		private Rectangle backboardrect;
		private Rectangle sideboardrect;
	
	//declare some useful constants
	//constants used by board state
		//indicates that the board is not ready
		//when the drawing mechanism sees this, it should switch state to SETUP
		public static final int UNSTARTED = 0;
		//indicates that the board is undergoing setup
		//when the drawing mechanism sees this, it should call the popup box to gather setup info
		public static final int SETUP = 1;
		//indicates that the board is undergoing piece placement
		//when the drawing mechanism sees this,
		// it should check if the movingpiece flag is up
		// and draw an instance of the current piece at mousepos if it is
		public static final int PLACEMENT = 2;
		//indicates that the board is in standard game play state
		//when the drawing mechanism sees this,
		// it should start by checking what view is being used
		// and checking which bord is being viewed
		// to determine what board to draw
		//then it should draw all the pieces that are on the board
		//  by checking what pieces are located on the 
		public static final int PLAY = 3;
		public static final int REPLAY = 4;
	//constants used by view state
		public static final int SIDE_VIEW = 0;
		public static final int BACK_VIEW = 1;
	
	//our constructor
	public GameBoard() {
		
		//call our ancestor
		super();
		
		//setup our background color
		this.setBackground(new Color(25, 122, 48));
		
		//initialize all our general variables
		this.initializeVars();
		
		//reset all our game data variables
		this.resetData();
		
		//setup input handling
		this.addMouseListener(this);
		this.addMouseMotionListener(this);
		
		//set a reasonable size to match the ratio of our splash image
		this.setPreferredSize(this.ourpreferredsize);
		
		//setup our visibilities
		this.setVisible(true);
		this.setOpaque(true);
		this.setFocusable(true);
		
	}
	
	//our complex overridden drawing mechanism
	protected void paintComponent(Graphics g) {
		
		//call our ancestor
		super.paintComponent(g);
		
		/*
		System.out.println("Report->>\n\t boardstate:" + this.getBoardState() 
			+ "\n\t viewstate:" + this.getViewState()
			+ "\n\t movingpiece:" + this.movingpiece
			+ "\n\t currentpiece:" + this.currentpiece
			+ "\n\t currentposition:" + this.currentposition
			+ "\n\t backboard:" + this.backboardrect
			+ "\n\t sideboard:" + this.sideboardrect
			+ "\n\t ourpreferred:" + this.ourpreferredsize
			+ "\n\t outscaled:" + this.getScaleSize(this.ourpreferredsize));
		*/
		
		//declare any temporary vars we may use repeatedly
		boolean lightsquare = true;
		String squarename = "";
		Dimension scalesize = null;
		Point scaleposition = null;
		String piecename = "";
		int pieceowner = 0;
		int index = 0;
		Point tempposition = null;
		
		//get our current scale size
		double scale = this.getScale();
		
		//keep this in focus
		//this.requestFocusInWindow();
		
		//if we are asked to paint but we are UNSTARTED
		// we should change the mode to SETUP and start the timer
		// so that it will fire off the game setup thread
		if (this.getBoardState() == GameBoard.UNSTARTED){
			this.setBoardState(GameBoard.SETUP);
			this.boardtimer.start();
		}
		
		//start our animation board timer if it is not started yet
		/*
		if (this.getBoardState() == GameBoard.PLACEMENT
			|| this.getBoardState() == GameBoard.PLAY
			|| this.getBoardState() == GameBoard.REPLAY){
			if (!this.boardtimer.isRunning()){
				this.boardtimer.start();
				System.out.println("Starting board timer...");
			}
		}
		*/
		
		//draw our board
		if (this.getBoardState() == GameBoard.PLACEMENT
			|| this.getBoardState() == GameBoard.PLAY
			|| this.getBoardState() == GameBoard.REPLAY){
			//generate scale instances now to save time
			scalesize = this.getSquareSize();
			index = this.imagenames.indexOf("BoardLightSquare");
			this.verifyScale(index, scalesize);
			index = this.imagenames.indexOf("BoardDarkSquare");
			this.verifyScale(index, scalesize);
			index = this.imagenames.indexOf("CurrentPlayerArrow");
			this.verifyScale(index, scalesize);
			index = this.imagenames.indexOf("PreviousBoardArrow");
			scalesize = this.getScaleSize(this.imagedims.get(index));
			this.verifyScale(index, scalesize);
			index = this.imagenames.indexOf("NextBoardArrow");
			scalesize = this.getScaleSize(this.imagedims.get(index));
			this.verifyScale(index, scalesize);
			//check what board view we are on
			if (this.getViewState() == GameBoard.SIDE_VIEW){
				//draw the appropriate board images
				//loop through every coordinate for each board and draw the square
				//remember to cycle the square color back and forth
				lightsquare = true;
				for (int y=0; y<this.boardsize.height; ++y){
					for (int x=0; x<this.boardsize.width; ++x){
						scaleposition = this.getPixelFromLocation(new GameBoard.Point3D(x,y,0));
						if (lightsquare){
							index = this.imagenames.indexOf("BoardLightSquare");
						}else{
							index = this.imagenames.indexOf("BoardDarkSquare");
						}
						this.drawCenteredImage(this.scaleimagerefs.get(index), this.previousscale.get(index), scaleposition, g);
						lightsquare = !lightsquare;
					}
					if ((boardsize.width % 2) == 0){
						lightsquare = !lightsquare;
					}
				}
				lightsquare = true;
				for (int y=0; y<this.boardsize.height; ++y){
					for (int x=0; x<this.boardsize.width; ++x){
						scaleposition = this.getPixelFromLocation(new GameBoard.Point3D(x,y,1));
						if (lightsquare){
							index = this.imagenames.indexOf("BoardLightSquare");
						}else{
							index = this.imagenames.indexOf("BoardDarkSquare");
						}
						this.drawCenteredImage(this.scaleimagerefs.get(index), this.previousscale.get(index), scaleposition, g);
						lightsquare = !lightsquare;
					}
					if ((boardsize.width % 2) == 0){
						lightsquare = !lightsquare;
					}
				}
				//draw player names text above and below the board
				//(5,80,390,190)
				g.setColor(Color.RED);
				g.setFont(this.prepareFont((int)Math.ceil(scale*this.sideboardrect.y / 3.0)));
				scaleposition.x = (int)Math.floor(scale*this.sideboardrect.x + (scale*this.sideboardrect.width * 4.0 / 20.0));
				scaleposition.y = (int)Math.floor(scale*this.sideboardrect.y * 4.0 / 5.0);
				g.drawString(this.playerlist.get(0), scaleposition.x, scaleposition.y);
				g.setColor(Color.BLACK);
				g.setFont(this.prepareFont((int)Math.ceil(scale*this.sideboardrect.y / 3.0)));
				scaleposition.x = (int)Math.floor(scale*this.sideboardrect.x + (scale*this.sideboardrect.width * 4.0 / 20.0));
				scaleposition.y = (int)Math.floor((scale*this.sideboardrect.y * 1.0 / 4.0) + scale*this.sideboardrect.height + scale*this.sideboardrect.y);
				g.drawString(this.playerlist.get(1), scaleposition.x, scaleposition.y);
				//draw current player arrow in front of the current player name
				index = this.imagenames.indexOf("CurrentPlayerArrow");
				scaleposition.x = (int)Math.floor(scale*this.sideboardrect.x + (scale*this.sideboardrect.width * 3.0 / 20.0));
				if (this.currentplayer == 0){
					scaleposition.y = (int)Math.floor(scale*this.sideboardrect.y * 7.0 / 10.0);
					this.drawCenteredImage(this.scaleimagerefs.get(index), this.previousscale.get(index), scaleposition, g);
				}else{
					scaleposition.y = (int)Math.floor((scale*this.sideboardrect.y * 2.0 / 12.0) + scale*this.sideboardrect.height + scale*this.sideboardrect.y);
					this.drawCenteredImage(this.scaleimagerefs.get(index), this.previousscale.get(index), scaleposition, g);
				}
			}else if (this.getViewState() == GameBoard.BACK_VIEW){
				//draw the appropriate board image
				//loop through every coordinate for each board and draw the square
				//remember to cycle the square color back and forth
				lightsquare = true;
				scalesize = this.getSquareSize();
				for (int y=0; y<this.boardsize.height; ++y){
					for (int x=0; x<this.boardsize.width; ++x){
						scaleposition = this.getPixelFromLocation(new GameBoard.Point3D(x,y,this.currentboard));
						if (lightsquare){
							index = this.imagenames.indexOf("BoardLightSquare");
						}else{
							index = this.imagenames.indexOf("BoardDarkSquare");
						}
						this.drawCenteredImage(this.scaleimagerefs.get(index), this.previousscale.get(index), scaleposition, g);
						lightsquare = !lightsquare;
					}
					if ((boardsize.width % 2) == 0){
						lightsquare = !lightsquare;
					}
				}
				//draw player names text above and below the board without underlapping arrow buttons
				//if player name is too long, it will always underlap
				//(50,25,300,300)
				//so set dimpoint (25,325,50,25)
				//so set dimpoint (325,325,50,25)
				g.setColor(Color.RED);
				g.setFont(this.prepareFont((int)Math.ceil(scale*this.backboardrect.y)));
				scaleposition.x = (int)Math.floor(scale*this.backboardrect.x + (scale*this.backboardrect.width * 3.0 / 10.0));
				scaleposition.y = (int)Math.floor(scale*this.backboardrect.y * 7.0 / 10.0);
				g.drawString(this.playerlist.get(0), scaleposition.x, scaleposition.y);
				g.setColor(Color.BLACK);
				g.setFont(this.prepareFont((int)Math.ceil(scale*this.backboardrect.y)));
				scaleposition.x = (int)Math.floor(scale*this.backboardrect.x + (scale*this.backboardrect.width * 3.0 / 10.0));
				scaleposition.y = (int)Math.floor(scale*this.backboardrect.height + (scale*this.backboardrect.y * 6.0 / 10.0) + scale*this.backboardrect.y);
				g.drawString(this.playerlist.get(1), scaleposition.x, scaleposition.y);
				//draw current player arrow in front of the current player name
				index = this.imagenames.indexOf("CurrentPlayerArrow");
				scaleposition.x = (int)Math.floor(scale*this.backboardrect.x + (scale*this.backboardrect.width * 2.0 / 10.0));
				if (this.currentplayer == 0){
					scaleposition.y = (int)Math.floor(scale*this.backboardrect.y * 5.0 / 10.0);
					this.drawCenteredImage(this.scaleimagerefs.get(index), this.previousscale.get(index), scaleposition, g);
				}else{
					scaleposition.y = (int)Math.floor(scale*this.backboardrect.height + (scale*this.backboardrect.y * 3.0 / 10.0) + scale*this.backboardrect.y);
					this.drawCenteredImage(this.scaleimagerefs.get(index), this.previousscale.get(index), scaleposition, g);
				}
				//when in back view, we also need to paint the buttons that allow us to move between boards
				index = this.imagenames.indexOf("PreviousBoardArrow");
				scaleposition = this.getScalePosition(this.imagepos.get(index));
				g.drawImage(this.scaleimagerefs.get(index), scaleposition.x, scaleposition.y, null);
				index = this.imagenames.indexOf("NextBoardArrow");
				scaleposition = this.getScalePosition(this.imagepos.get(index));
				g.drawImage(this.scaleimagerefs.get(index), scaleposition.x, scaleposition.y, null);
			}
		}
		
		//draw our pieces on the board
		if (this.getBoardState() == GameBoard.PLACEMENT
			|| this.getBoardState() == GameBoard.PLAY){
			//draw the pieces if they are not null
			// if the piece type is a checker or king
			// look up the associated pieceowners value
			// to determine if we draw red or black
			//generate scale instances now to save time
			scalesize = this.getSquareSize();
			index = this.imagenames.indexOf("RedChecker");
			this.verifyScale(index, scalesize);
			index = this.imagenames.indexOf("BlackChecker");
			this.verifyScale(index, scalesize);
			index = this.imagenames.indexOf("RedKing");
			this.verifyScale(index, scalesize);
			index = this.imagenames.indexOf("BlackKing");
			this.verifyScale(index, scalesize);
			index = this.imagenames.indexOf("Mine");
			this.verifyScale(index, scalesize);
			index = this.imagenames.indexOf("SafeZone");
			this.verifyScale(index, scalesize);
			index = this.imagenames.indexOf("BlockedZone");
			this.verifyScale(index, scalesize);
			//check which board view we are using
			if (this.getViewState() == GameBoard.SIDE_VIEW){
				//loop through our piecetypes combination of arraylists for each board
				scalesize = this.getSquareSize();
				scanx: for (int x=0; x<this.boardsize.width; ++x){
					scany: for (int y=0; y<this.boardsize.height; ++y){
						scanboard: for (int z=0; z<2; ++z){
							scanpiece: for (int p=0; p<piecetypes.get(x).get(y).get(z).size(); ++p){
								scaleposition = this.getPixelFromLocation(new GameBoard.Point3D(x,y,z));
								piecename = piecetypes.get(x).get(y).get(z).get(p);
								//do not draw mine
								if (!piecename.equals("Mine")){
									if (piecename.equals("Checker") || piecename.equals("King")){
										pieceowner = pieceowners.get(x).get(y).get(z).get(p).intValue();
										if (pieceowner == 0){
											piecename = "Red" + piecename;
										}else{
											piecename = "Black" + piecename;
										}
									}
									index = this.imagenames.indexOf(piecename);
									this.drawCenteredImage(this.scaleimagerefs.get(index), this.previousscale.get(index), scaleposition, g);
								}
							}
						}
					}
				}
			}else if (this.getViewState() == GameBoard.BACK_VIEW){
				//loop through our piecetypes combination of arraylists
				// for the single board that we are looking at
				scalesize = this.getSquareSize();
				scanx: for (int x=0; x<this.boardsize.width; ++x){
					scany: for (int y=0; y<this.boardsize.height; ++y){
						//currentposition is unreliable as record of what the user is looking at
						// because we use it for moving pieces
						//int z = this.currentposition.z;
						int z = this.currentboard;
						scanpiece: for (int p=0; p<piecetypes.get(x).get(y).get(z).size(); ++p){
							scaleposition = this.getPixelFromLocation(new GameBoard.Point3D(x,y,z));
							piecename = piecetypes.get(x).get(y).get(z).get(p);
							//do not draw mine
							if (!piecename.equals("Mine")){
								if (piecename.equals("Checker") || piecename.equals("King")){
									pieceowner = pieceowners.get(x).get(y).get(z).get(p).intValue();
									if (pieceowner == 0){
										piecename = "Red" + piecename;
									}else{
										piecename = "Black" + piecename;
									}
								}
								index = this.imagenames.indexOf(piecename);
								this.drawCenteredImage(this.scaleimagerefs.get(index), this.previousscale.get(index), scaleposition, g);
							}
						}
					}
				}
			}
		}else if (this.getBoardState() == GameBoard.REPLAY){
			//TBD
			//draw the pieces if they are not null
			// if the piece type is a checker or king
			// look up the associated pieceowners value
			// to determine if we draw red or black
			//generate scale instances now to save time
			scalesize = this.getSquareSize();
			index = this.imagenames.indexOf("RedChecker");
			this.verifyScale(index, scalesize);
			index = this.imagenames.indexOf("BlackChecker");
			this.verifyScale(index, scalesize);
			index = this.imagenames.indexOf("RedKing");
			this.verifyScale(index, scalesize);
			index = this.imagenames.indexOf("BlackKing");
			this.verifyScale(index, scalesize);
			index = this.imagenames.indexOf("Mine");
			this.verifyScale(index, scalesize);
			index = this.imagenames.indexOf("SafeZone");
			this.verifyScale(index, scalesize);
			index = this.imagenames.indexOf("BlockedZone");
			this.verifyScale(index, scalesize);
			//loop through our piecetypes combination of arraylists for each board
			scalesize = this.getSquareSize();
			scanx: for (int x=0; x<this.boardsize.width; ++x){
				scany: for (int y=0; y<this.boardsize.height; ++y){
					scanboard: for (int z=0; z<2; ++z){
						scanpiece: for (int p=0; p<replaytypes.get(x).get(y).get(z).size(); ++p){
							scaleposition = this.getPixelFromLocation(new GameBoard.Point3D(x,y,z));
							piecename = replaytypes.get(x).get(y).get(z).get(p);
							//do not draw mine
							if (!piecename.equals("Mine")){
								if (piecename.equals("Checker") || piecename.equals("King")){
									pieceowner = replayowners.get(x).get(y).get(z).get(p).intValue();
									if (pieceowner == 0){
										piecename = "Red" + piecename;
									}else{
										piecename = "Black" + piecename;
									}
								}
								index = this.imagenames.indexOf(piecename);
								this.drawCenteredImage(this.scaleimagerefs.get(index), this.previousscale.get(index), scaleposition, g);
							}
						}
					}
				}
			}
		}
		
		//draw a highlight on the location of the currently selected piece
		//don't do this if we don't have a currentpice name registered
		if (this.getBoardState() == GameBoard.PLAY && !this.currentpiece.equals("")){
			//generate scale instances now to save time
			scalesize = this.getSquareSize();
			index = this.imagenames.indexOf("Selected");
			this.verifyScale(index, scalesize);
			scaleposition = this.getPixelFromLocation(this.previousposition);
			//check which board view we are using
			if (this.getViewState() == GameBoard.SIDE_VIEW){
				index = this.imagenames.indexOf("Selected");
				this.drawCenteredImage(this.scaleimagerefs.get(index), this.previousscale.get(index), scaleposition, g);
			}else if (this.getViewState() == GameBoard.BACK_VIEW){
				//make sure the selected position is for the board we are currently looking at
				if (this.previousposition.z == this.currentboard){
					scaleposition = this.getPixelFromLocation(this.previousposition);
					index = this.imagenames.indexOf("Selected");
					this.drawCenteredImage(this.scaleimagerefs.get(index), this.previousscale.get(index), scaleposition, g);
				}
			}
		}
		
		//draw our available move hints
		//check for the showmoves flag
		if (this.showmoves && this.getBoardState() == GameBoard.PLAY){
			//generate scale instances now to save time
			scalesize = this.getSquareSize();
			index = this.imagenames.indexOf("MoveHint");
			this.verifyScale(index, scalesize);
			//check which board view we are using
			if (this.getViewState() == GameBoard.SIDE_VIEW){
				//loop through our move hints drawing highlights on the boards where needed
				for (int i=0; i<this.availablemoves.size(); ++i){
					scaleposition = this.getPixelFromLocation(this.availablemoves.get(i));
					index = this.imagenames.indexOf("MoveHint");
					this.drawCenteredImage(this.scaleimagerefs.get(index), this.previousscale.get(index), scaleposition, g);
				}
			}else if (this.getViewState() == GameBoard.BACK_VIEW){
				//loop through our move hints drawing highlights if they appear on the current board
				for (int i=0; i<this.availablemoves.size(); ++i){
					//make sure this hint is for the current board
					if (this.availablemoves.get(i).z == this.currentboard){
						scaleposition = this.getPixelFromLocation(this.availablemoves.get(i));
						index = this.imagenames.indexOf("MoveHint");
						this.drawCenteredImage(this.scaleimagerefs.get(index), this.previousscale.get(index), scaleposition, g);
					}
				}
			}
		}
		
		//draw our pieces as we move them on the board for placement
		if (this.getBoardState() == GameBoard.PLACEMENT
			|| this.getBoardState() == GameBoard.PLAY){
			if (this.movingpiece){
				//we already generated scale instances when drawing pieces on board
				//check what piece we are looking at
				scalesize = this.getSquareSize();
				piecename = this.currentpiece;
				if (piecename.equals("Checker") || piecename.equals("King")){
					pieceowner = this.currentplayer;
					if (pieceowner == 0){
						piecename = "Red" + piecename;
					}else{
						piecename = "Black" + piecename;
					}
				}
				index = this.imagenames.indexOf(piecename);
				//adjust so that piece will be centered over mouse point
				scaleposition = new Point(mousepos);
				scaleposition.translate(0 - (int)(scalesize.width / 2.0), 0 - (int)(scalesize.height / 2.0));
				g.drawImage(this.scaleimagerefs.get(index), scaleposition.x, scaleposition.y, null);
			}
		}
		
		//draw animations if any
		// will be used with replay mode
		//check if we are in replay mode
		if (this.getBoardState() == GameBoard.REPLAY
			||this.getBoardState() == GameBoard.PLAY){
			//need to handle the animation process here for showing the current animation frame
			//TBD
			//check if we have something to animate currently
			if (this.animindex >= 0){
				//generate scale instances now to save time
				scalesize = this.stepdim;
				scaleposition = this.steppos;
				this.verifyScale(animindex, scalesize);
				System.out.println("try to draw " + this.imagenames.get(this.animindex) + " with size: " + scalesize + " at position: " + scaleposition);
				this.drawCenteredImage(this.scaleimagerefs.get(this.animindex), this.previousscale.get(this.animindex), scaleposition, g);
				//g.drawImage(this.scaleimagerefs.get(animindex), scaleposition.x, scaleposition.y, null);
			}
		}
		
	}
	
	//reset for list of players
	public void clearPlayers() {
		
		this.playerlist = new ArrayList<String>();
		
	}
	/*
	//increment for list of players
	public void addPlayer(String playername) {
		
		this.playerlist.add(playername);
		
	}
	*/
	//mutator for list of players
	public void setGamePlayers(ArrayList<String> players) {
		
		this.playerlist = players;
		
	}
	
	//mutator for show moves
	public void setShowMoves(boolean value) {
		
		this.showmoves = value;
		//update game buttons
		try{
			((GameView)UIController.grabState("GameView")).gamebuttons.updateShowMoves(value);
		}catch (NullPointerException EXC){
			//GameView not instantiated yet
		}
		//this will change our display so we should redraw
		this.repaint();
		
	}
	
	//method to setup the game by retrieving board data and current player from Logic
	// this will bypass the normal board setup and piece placement
	public void prepareLoadGame(ArrayList<String> tmpplayers) {
		
		//set our players
		/*
		ArrayList<String> orderedplayers = new ArrayList<String>();
		String firstplay = UIController.getFirstPlayer();
		orderedplayers.add(firstplay);
		orderedplayers.add(tmpplayers.get((tmpplayers.indexOf(firstplay) + 1) % 2));
		*/
		this.setGamePlayers(tmpplayers);
		//set our first player
		this.firstplayer = this.playerlist.indexOf(UIController.getFirstPlayer());
		//set our current player
		this.currentplayer = this.playerlist.indexOf(UIController.getGamePlayer());
		//setup data
		this.boardsize = UIController.getBoardSize();
		this.boardcount = 2;
		this.playercount = 2;
		//jump directly into play mode, do not pass go, do not collect 200 dollars
		// required before syncing board to make sure it doesn't think we are in setup and want to start from the first player
		this.setBoardState(GameBoard.PLAY);
		//update our boards to match the loaded boards
		this.syncBoardData();
		//make sure we use the current player
		//this is awkward because we need to call next play turn which will call next player
		//... we should use previous player to cycle backwards 1
		this.previousPlayer();
		this.nextPlayTurn();
		//kickstart our board timer
		this.boardtimer.start();
		
	}
	
	//accessor/mutator for board state
	public void setBoardState(int newstate) {
		
		int prevstate = boardstate;
		//make sure someone isnt using a bad state
		if (newstate != GameBoard.UNSTARTED &&
			newstate != GameBoard.SETUP &&
			newstate != GameBoard.PLACEMENT &&
			newstate != GameBoard.PLAY &&
			newstate != GameBoard.REPLAY){
			boardstate = UNSTARTED;
		}else{
			boardstate = newstate;
		}
		//perform mode-specific changes
		if (boardstate == GameBoard.SETUP){
			//tell buttons to switch
			((GameView)UIController.grabState("GameView")).gamebuttons.displayGameSetup();
		}else if (boardstate == GameBoard.PLACEMENT){
			//tell buttons to update
			((GameView)UIController.grabState("GameView")).gamebuttons.getPieceCounts();
		}else if (boardstate == GameBoard.PLAY){
			//tell buttons to switch
			((GameView)UIController.grabState("GameView")).gamebuttons.displayGamePlay();
			//if we came from setup or placement then it indicates we are done setting up
			if (prevstate == GameBoard.SETUP || prevstate == GameBoard.PLACEMENT){
				//tell logic to save the current board states as the intial game boards
				UIController.initializeBoards();
			}
		}
		//redraw when someone tries to change the state
		this.repaint();
		
	}
	public int getBoardState() {
		
		return boardstate;
		
	}
	
	//accessor/mutator for view state
	public void setViewState(int newstate) {
		
		viewstate = newstate;
		//redraw when someone tries to change the state
		this.repaint();
		
	}
	public int getViewState() {
		
		return viewstate;
		
	}
	
	//Methods called by GameMenu
	//method used to change the view to the Back-to-Back style
	public void viewBacktoBack() {
		
		//force side view when in replay mode
		if (this.getBoardState() != GameBoard.REPLAY) {
			//change the view flag so that the drawing mechanism knows what to draw
			this.setViewState(GameBoard.BACK_VIEW);
			//make sure we paint ourselves
			this.repaint();
		}else{
			((GameView)UIController.grabState("GameView")).gamemenu.selectBoardView("Side");
		}
		
	}
	//method used to change the view to the Side-by-Side style
	public void viewSidebySide() {
		
		//change the view flag so that the drawing mechanism knows what to draw
		this.setViewState(GameBoard.SIDE_VIEW);
		//make sure we paint ourselves
		this.repaint();
		
	}
	//method used to start a new game if the players agree
	public boolean newGame() {
		
		boolean useragree = this.askYNQuestion( 
			playerlist.get(0) + ", do you agree to discard this game and start a new one?",
			"New Game?");
		if (useragree) {
			useragree = this.askYNQuestion( 
				playerlist.get(1) + ", do you agree to discard this game and start a new one?",
				"New Game?");
			if (useragree){
				//reset data if the user says yes
				this.resetData();
				//make sure we tell game to reset data as well
				//UIController.newGame();
				//nvm, set Game Players will call a new game
				//we should make sure the new game retains the players
				UIController.setGamePlayers(this.playerlist);
			}
		}
		return useragree;
		
	}
	//method used to load a game if the players agree
	public boolean loadGame() {
		
		boolean useragree = this.askYNQuestion( 
			playerlist.get(0) + ", do you agree to discard this game and load a different one?",
			"Load Game?");
		if (useragree) {
			useragree = this.askYNQuestion( 
				playerlist.get(1) + ", do you agree to discard this game and load a different one?",
				"Load Game?");
			if (useragree){
				//reset data if the user says yes
				this.resetData();
				//make sure we tell game to reset data as well
				UIController.newGame();
				//we should make sure the new game retains the players
				//nvm, GameLoad will call set players
				//UIController.setGamePlayers(this.playerlist);
				//inform GameLoad of the players
				try{
					((GameLoad)UIController.grabState("GameLoad")).getGames(this.playerlist.get(0), this.playerlist.get(1));
				}catch (NullPointerException EXC){
					System.out.println("Game Board failed to gain instance of GameLoad");
				}
			}
		}
		return useragree;
		
	}
	//method used to try to save the current game and let the users know if the save succeeded
	public void saveGame() {
		
		//only allow the game to be saved if we are safely in gameplay
		if (this.getBoardState() == GameBoard.PLAY){
			boolean success = UIController.saveGame();
			if (success){
				//this.showInfoNotice("The game has been successfully saved!", "Game Saved");
				UIController.printPopup("The game has been successfully saved!", 10.5);
			}else{
				//this.showInfoNotice("The game has failed to save!", "Unexpected Error!");
				UIController.printPopup("The game has failed to save!", 15.5);
			}
		}
		
	}
	//method to close the current game and potentially save
	public boolean closeGame() {
		
		//only allow the game to be saved if we are safely in gameplay
		if (this.getBoardState() == GameBoard.PLAY){
			int userdecide = this.askYNCQuestion( 
				"Would you like to save the game before returning to the main menu?",
				"Save Game?");
			if (userdecide == 0){
				//code needed here to save
				this.saveGame();
				//reset data if the user says yes
				this.resetData();
				//make sure we tell game to reset data as well
				//UIController.newGame();
				return true;
			}else if (userdecide == 1){
				//reset data if the user says yes
				this.resetData();
				//make sure we tell game to reset data as well
				//UIController.newGame();
				return true;
			}else{
				return false;
			}
		}else{
			boolean useragree = this.askYNQuestion( 
			"Are you sure you want to return to main menu and lose any unsaved data?",
			"Return to Menu?");
			if (useragree) {
				UIController.printPopup("Returning to main menu...", 8.0);
				//discard data if the user says yes
				this.resetData();
				//make sure we tell game to discard data as well
				//UIController.newGame();
			}
			return useragree;
		}
		
	}
	//method to close the current game and potentially save
	public boolean exitGame() {
		
		//only allow the game to be saved if we are safely in gameplay
		if (this.getBoardState() == GameBoard.PLAY){
			int userdecide = this.askYNCQuestion( 
				"Would you like to save the game before exitting the program?",
				"Save Game?");
			if (userdecide == 0){
				//code needed here to save
				this.saveGame();
				return true;
			}else if (userdecide == 1){
				return true;
			}else{
				return false;
			}
		}else{
			boolean useragree = this.askYNQuestion( 
			"Are you sure you want to exit the program and lose any unsaved data?",
			"Return to Menu?");
			if (useragree) {
				//discard data if the user says yes
				this.resetData();
				//make sure we tell game to discard data as well
				//UIController.newGame();
			}
			return useragree;
		}
		
	}
	//method used to request if the user wants to discard all game data
	//if yes, all data will be reset to defaults
	public boolean discard() {
		
		boolean useragree = this.askYNQuestion( 
			"Are you sure you want to discard this game and lose any unsaved data?",
			"Discard Game?");
		if (useragree) {
			UIController.printPopup("Discarding game data...", 4.8);
			//discard data if the user says yes
			this.resetData();
			//make sure we tell game to discard data as well
			//UIController.newGame();
		}
		return useragree;
		
	}
	
	//Methods called by GameButtons
	//method used to prepare for the user to place a checker
	public void placeChecker() {
		
		//make sure we are in PLACEMENT mode
		if (this.getBoardState() == GameBoard.PLACEMENT){
			//set the type of piece we are now looking at
			this.currentpiece = "Checker";
			//set the moving piece flag for the draw mechanism
			this.movingpiece = true;
			//keep this in focus
			this.requestFocusInWindow();
		}
		
	}
	//method used to prepare for the user to place a king
	public void placeKing() {
		
		//make sure we are in PLACEMENT mode
		if (this.getBoardState() == GameBoard.PLACEMENT){
			//set the type of piece we are now looking at
			this.currentpiece = "King";
			//set the moving piece flag for the draw mechanism
			this.movingpiece = true;
			//keep this in focus
			this.requestFocusInWindow();
		}
		
	}
	//method used to prepare for the user to place a mine
	public void placeMine() {
		
		//not used until later builds
		//this.showInfoNotice("Mines are disabled in this version.", "Feature not implemented.");
		//make sure we are in PLACEMENT mode
		if (this.getBoardState() == GameBoard.PLACEMENT){
			//set the type of piece we are now looking at
			this.currentpiece = "Mine";
			//set the moving piece flag for the draw mechanism
			this.movingpiece = true;
			//keep this in focus
			this.requestFocusInWindow();
		}
		
	}
	//method used to prepare for the user to place a BlockedZone
	public void placeBlockedZone() {
		
		//not used until later builds
		//this.showInfoNotice("BlockedZones are disabled in this version.", "Feature not implemented.");
		//make sure we are in PLACEMENT mode
		if (this.getBoardState() == GameBoard.PLACEMENT){
			//set the type of piece we are now looking at
			this.currentpiece = "BlockedZone";
			//set the moving piece flag for the draw mechanism
			this.movingpiece = true;
			//keep this in focus
			this.requestFocusInWindow();
		}
		
	}
	//method used to prepare for the user to place a safezone
	public void placeSafeZone() {
		
		//not used until later builds
		//this.showInfoNotice("SafeZones are disabled in this version.", "Feature not implemented.");
		//make sure we are in PLACEMENT mode
		if (this.getBoardState() == GameBoard.PLACEMENT){
			//set the type of piece we are now looking at
			this.currentpiece = "SafeZone";
			//set the moving piece flag for the draw mechanism
			this.movingpiece = true;
			//keep this in focus
			this.requestFocusInWindow();
		}
		
	}
	//method used to take the current move and ask the UIController to actually make it
	//  Boolean makeMove(ArrayList<Point>, ArrayList<Boolean>, ArrayList<String>, String)
	public void confirmMove() {
		
		//this is discontinued for now
		/*
		//piece should have the format "PieceName,OwnerName,XCoord,YCoord,ZCoord"
		String pieceinfo = "";
		pieceinfo += this.currentpiece + ",";
		pieceinfo += this.playerlist.get(this.currentplayer) + ",";
		pieceinfo += this.currentposition.x + ",";
		pieceinfo += this.currentposition.y + ",";
		pieceinfo += this.currentposition.z;
		int size = this.currentmove.size();
		ArrayList<Point> coordlist = new ArrayList<Point>(size);
		ArrayList<Integer> boardlist = new ArrayList<Integer>(size);
		for(int i=0; i<size; ++i){
			coordlist.add(new Point(this.currentmove.get(i).x, this.currentmove.get(i).y));
			boardlist.add(new Integer(this.currentmove.get(i).z));
		}
		
		UIController.makeMove(coordlist, boardlist, pieceinfo);
		*/
		
	}
	//method used to toggle the flag for showing pieces that have moves
	public void showMoves() {
		
		//invert the boolean flag
		this.setShowMoves(!this.showmoves);
		
	}
	//method used to toggle the board state between PLAY and REPLAY
	public void showReplay() {
		
		//this.showInfoNotice("Replays are disabled in this version.", "Feature not implemented.");
		///*
		//button clicked, check what mode we are in
		if (this.getBoardState() == GameBoard.PLAY) {
			//pause animation
			this.boardtimer.stop();
			//ask the user how many moves to go back
			int goback = -1;
			while (goback <= 0){
				try{
					goback = Integer.parseInt(this.requestInfo(
						"Enter the number of turns you would like to go back."
						+ "\nYou must enter a positive integer larger than 0.", "Set Replay Parameter"));
				}catch (NumberFormatException EXC){
					this.boardtimer.start();
					return;
				}
			}
			//force side view while in replays
			((GameView)UIController.grabState("GameView")).gamemenu.selectBoardView("Side");
			this.setViewState(GameBoard.SIDE_VIEW);
			this.repaint();
			UIController.printPopup("The replay has started...!", 12.5);
			//setup the replay gracefully
			try{
				this.getReplayData(goback);
			}catch (Exception EXC){
				System.out.println("Replays crashed most likely because of a fault in the data it received...");
				System.out.println(EXC + "\n");
				EXC.printStackTrace();
				this.boardtimer.start();
				//let the user know we had a hiccup
				/*
				this.showInfoNotice("The replay has encountered a problem!"
					+ "\nThis was most likely caused by corrupted replay data.",
					"Replay Failure");
				*/
				UIController.printPopup("The replay has encountered a problem!!!"
					+ "<br>This was most likely caused by corrupted replay data...", 18.0);
				return;
			}
			//expect the above method to have started replays back up by default
			//this should now be only a redundant safety
			this.boardtimer.stop();
			//set new mode
			this.setBoardState(GameBoard.REPLAY);
			//update button
			((GameView)UIController.grabState("GameView")).gamebuttons.updateShowReplay(true);
			//start animation
			this.boardtimer.start();
		}else if (this.getBoardState() == GameBoard.REPLAY) {
			//pause animation
			this.boardtimer.stop();
			//verify stop replay
			boolean useragree = this.askYNQuestion(
				"Are you sure you want to stop the replay?"
				+ "  You will lose your current position in the replay.",
				"Stop Replay?");
			if (useragree){
				UIController.printPopup("The replay has been stopped...", 13.5);
				//stop replay
				this.setBoardState(GameBoard.PLAY);
				//update button
				((GameView)UIController.grabState("GameView")).gamebuttons.updateShowReplay(false);
			}
			//start animation
			this.boardtimer.start();
		}
		//*/
		
	}
	//method used to allow the user to forfeit the game
	public void forfeitGame() {
		
		//not used until later Builds
		//this.showInfoNotice("Forfeit Game is disabled in this version.", "Feature not implemented.");
		
		if (this.getBoardState() == GameBoard.PLAY) {
			boolean useragree = this.askYNQuestion(
				this.playerlist.get(this.currentplayer)
				+ ", do you really want to forfeit this game?"
				+ "  It will be the same as if you lost the game.",
				"Forfeit Game?");
			if (useragree){
				//follow forfeit procedures here
				UIController.forfeitGame(this.playerlist.get(this.currentplayer));
				//TBD
				//this.askNewGame();
				UIController.printPopup("Reviewing game...", 10.5);
				//jump to game review
				try{
					((GameReview)UIController.grabState("GameReview")).setPlayers(this.playerlist);
					((GameReview)UIController.grabState("GameReview")).setWinner(this.playerlist.get((this.currentplayer + 1) % this.playerlist.size()));
				}catch (NullPointerException EXC){
					System.out.println("Game Board failed to gain instance of GameReview");
				}
				//reset data since game has ended
				this.resetData();
				UIController.changeState("GameReview");
			}
		}
		
		
	}
	//method used to allow the user to request a draw
	public void callDraw() {
		
		//not used until later Builds
		//this.showInfoNotice("Call Draw is disabled in this version.", "Feature not implemented.");
		
		if (this.getBoardState() == GameBoard.PLAY) {
			//ask current player if they really want to call draw
			boolean useragree = this.askYNQuestion(
				this.playerlist.get(this.currentplayer)
				+ ", do you want to call a draw?"
				+ "  It will be the same as if you neither lost nor won the game.",
				"Call Draw?");
			if (useragree){
				//ask other players if they agree to the draw
				boolean otheragree = false;
				askothers: for (int i=0; i<playerlist.size(); ++i){
					if (i != this.currentplayer){
						//ask player i if they agree
						otheragree = this.askYNQuestion(
							this.playerlist.get(i) + ", do you agree to a draw?"
							+ "  It will be the same as if you neither lost nor won the game.",
							"Agree to Draw?");
						if (!otheragree)
							break askothers; 
					}
				}
				if (otheragree){
					//follow tie procedures here
					UIController.callDraw();
					//TBD
					//this.askNewGame();
					UIController.printPopup("Reviewing game...", 10.5);
					//jump to game review
					try{
						((GameReview)UIController.grabState("GameReview")).setPlayers(this.playerlist);
						((GameReview)UIController.grabState("GameReview")).setWinner(null);
					}catch (NullPointerException EXC){
						System.out.println("Game Board failed to gain instance of GameReview");
					}
					//reset data since game has ended
					this.resetData();
					UIController.changeState("GameReview");
				}
			}
		}
		
	}
	
	//hidden convenience method used to prepare a standard font depending on the pixel height available
	private Font prepareFont(int height) {
		
		int pointsize = (int)Math.ceil(height * 0.8);
		return new Font("Courier New", Font.PLAIN, pointsize);
		
	}
	
	//hidden convenience method for drawing an image onto a Graphics object
	// centered at a specified point based on image width and height
	private void drawCenteredImage(Image imgref, Dimension imgdim, Point imgpos, Graphics g) {
		
		//find new position
		Point newimgpos = new Point(imgpos);
		newimgpos.x = newimgpos.x - (int)Math.floor(imgdim.width/2.0);
		newimgpos.y = newimgpos.y - (int)Math.floor(imgdim.height/2.0);
		//draw
		g.drawImage(imgref, newimgpos.x, newimgpos.y, null);
		
	}
	
	
	//hidden convenience method for figuring out what scale our component is at
	// compared to its original size
	private double getScale() {
		
		//check how big our component is so that we can scale properly
		Dimension oursize = new Dimension();
		this.getSize(oursize);
		Dimension prefersize = this.ourpreferredsize;
		//our scale should be the ratio of our current size to our preferred size
		//to keep scale 1:1, we should take the smaller of the 2 dimensions
		double scalex = oursize.getWidth() / prefersize.getWidth();
		double scaley = oursize.getHeight() / prefersize.getHeight();
		double scale = scalex;
		if (scaley < scale){
			scale = scaley;
		}
		//System.out.println("scale: " + oursize + " <-> " + prefersize + " :: " + scale);
		return scale;
		
	}
	
	//hidden convenience method to scale the image at the index if necessary
	private void verifyScale(int index, Dimension scalesize) {
		
		if (!scalesize.equals(this.previousscale.get(index)) || (this.currentframe.get(index) != -1)){
			this.scaleimagerefs.set(index, 
				this.imagerefs.get(index).getScaledInstance(
					scalesize.width, scalesize.height, this.scalequality));
			this.previousscale.set(index, new Dimension(scalesize));
		}
		
	}
	
	//hidden method that initializes important variables
	// that are only required to be defined once
	private void initializeVars() {
		
		//declare any temporary objects we ened
		Image tempimg = null;
		String tempname = "";
		Dimension scalesize = null;
		
		//data reset that is only necessary at the start
		this.clearPlayers();
		
		//vars used for imaging
		this.ourpreferredsize = new Dimension(400,350);
		//board area relative to the component preferred size (400,350)
			//(50,25,300,300)
			this.backboardrect = new Rectangle((int)(this.ourpreferredsize.width * ((double)1.0/8.0)),
										(int)(this.ourpreferredsize.height * ((double)1.0/14.0)),
										(int)(this.ourpreferredsize.width * ((double)3.0/4.0)),
										(int)(this.ourpreferredsize.height * ((double)6.0/7.0)));
			//(5,80,390,190)
			this.sideboardrect = new Rectangle((int)(this.ourpreferredsize.width * ((double)1.0/80.0)), 
										(int)(this.ourpreferredsize.height * ((double)8.0/35.0)), 
										(int)(this.ourpreferredsize.width * ((double)39.0/40.0)), 
										(int)(this.ourpreferredsize.height * ((double)19.0/35.0)));
		//define image quality when scaling up or down
		this.scalequality = Image.SCALE_SMOOTH;
		//build image property lists
		this.imagenames = new ArrayList<String>();
		this.imagelocs = new ArrayList<String>();
		this.imagedims = new ArrayList<Dimension>();
		this.imagepos = new ArrayList<Point>();
		this.imagerefs = new ArrayList<BufferedImage>();
		this.animaterefs = new ArrayList<ArrayList<BufferedImage>>();
		this.currentframe = new ArrayList<Integer>();
		//add images and define properties
		//add Red Checker
		this.prepareImage("RedChecker", "images\\RedChecker.png", 
			new Dimension(30,30), new Point(0,0), true, 1.0);
		//add Black Checker
		this.prepareImage("BlackChecker", "images\\BlackChecker.png", 
			new Dimension(30,30), new Point(0,0), true, 1.0);
		//add Red King
		this.prepareImage("RedKing", "images\\RedKing.png", 
			new Dimension(30,30), new Point(0,0), true, 1.0);
		//add Black King
		this.prepareImage("BlackKing", "images\\BlackKing.png", 
			new Dimension(30,30), new Point(0,0), true, 1.0);
		//add Mine
		this.prepareImage("Mine", "images\\Mine.png", 
			new Dimension(30,30), new Point(0,0), true, 1.0);
		//add Blocked Zone
		this.prepareImage("BlockedZone", "images\\BlockedZone.png", 
			new Dimension(30,30), new Point(0,0), true, 1.0);
		//add Safe Zone
		this.prepareImage("SafeZone", "images\\SafeZone.png", 
			new Dimension(30,30), new Point(0,0), true, 1.0);
		//add Board Dark Square
		this.prepareImage("BoardDarkSquare", "images\\BoardDarkSquare.png", 
			new Dimension(30,30), new Point(0,0), true, 1.0);
		//add Board Light Square
		this.prepareImage("BoardLightSquare", "images\\BoardLightSquare.png", 
			new Dimension(30,30), new Point(0,0), true, 1.0);
		//add Move Hint highlight
		this.prepareImage("MoveHint", "images\\MoveHint_anim.gif", 
			new Dimension(30,30), new Point(0,0), true, 2.5);
		//add Selected highlight
		this.prepareImage("Selected", "images\\Selected_anim.gif", 
			new Dimension(30,30), new Point(0,0), true, 2.5);
		//add Previous Board Arrow
		// used for back board (50,25,300,300)
		//so set dimpoint (25,325,50,25)
		this.prepareImage("PreviousBoardArrow", "images\\PreviousBoardArrow.png", 
			new Dimension((int)(backboardrect.width * ((double)1.0/6.0)),
							(int)(backboardrect.height * ((double)1.0/12.0))), 
			new Point((int)(backboardrect.x * ((double)1.0/2.0)),
						(int)(backboardrect.y * ((double)13.0/1.0))),
			true, 1.0);
		//add Next Board Arrow
		// used for back board (50,25,300,300)
		//so set dimpoint (325,325,50,25)
		this.prepareImage("NextBoardArrow", "images\\NextBoardArrow.png", 
			new Dimension((int)(backboardrect.width * ((double)1.0/6.0)),
							(int)(backboardrect.height * ((double)1.0/12.0))), 
			new Point((int)(backboardrect.x * ((double)13.0/2.0)),
						(int)(backboardrect.y * ((double)13.0/1.0))),
			true, 1.0);
		//add Destroy animation
		this.prepareImage("Destroy", "images\\Destroy2.gif", 
			new Dimension(30,30), new Point(0,0), false, 1.0);
		//add Explosion animation
		this.prepareImage("Explosion", "images\\Explosion.gif", 
			new Dimension(60,60), new Point(0,0), false, 1.0);
		//add Current Player Arrow
		this.prepareImage("CurrentPlayerArrow", "images\\CurrentPlayerArrow.png", 
			new Dimension(30,30), new Point(0,0), true, 1.0);
		
		//setup scaled image data
		//no scale needed initially, copy refs
		this.scaleimagerefs = new ArrayList<Image>();
		this.previousscale = new ArrayList<Dimension>();
		//scalesize = this.getSquareSize();
		for (int i=0; i<this.imagerefs.size(); ++i){
			this.scaleimagerefs.add(this.imagerefs.get(i));
			//default scale
			this.previousscale.add(new Dimension(1,1));
		}
		
	}
	
	//hidden method that resets all data to defaults
	// that are commonly used at the start of a game
	private void resetData() {
		
		//state resets
		this.setBoardState(GameBoard.UNSTARTED);
		this.setViewState(GameBoard.SIDE_VIEW);
		this.hasclicked = false;
		//tell game menu to match the view state
		try{
			((GameView)UIController.grabState("GameView")).gamemenu.selectBoardView("side");
		}catch (NullPointerException EXC){
			//GameView not instantiated yet
			System.out.println("GameView not instantiated yet, request denied");
		}
		
		//replay resets
		this.replaytypes = new ArrayList<ArrayList<ArrayList<ArrayList<String>>>>();
		this.replayowners = new ArrayList<ArrayList<ArrayList<ArrayList<Integer>>>>();
		this.replayactions = new ArrayList<String>();
		this.replaymoves = new ArrayList<ArrayList<ArrayList<String>>>();
		this.replaymov = 0;
		this.replaymovepos = 0;
		this.replayactpos = 0;
		this.animationstep = 0;
		this.animsteps = -1;
		this.replayowner = 0;
		this.animdims = new ArrayList<Dimension>();
		this.animpos = new ArrayList<Point>();
		this.animindex = -1;
		this.stepdim = new Dimension();
		this.steppos = new Point();
		
		//action resets
		this.mousepos = new Point();
		this.movingpiece = false;
		
		//animation resets
		//fps higher than actual because of delays in calculation from timer-blocking
		this.fps = 20;
		if (this.boardtimer != null){
			this.boardtimer.stop();
		}
		this.boardtimer = new Timer((int)(1000 / fps), this);
		
		//hint resets
		this.setShowMoves(false);
		this.movehints = new ArrayList<GameBoard.Point3D>();
		
		//data resets
		this.currentboard = 0;
		this.boardsize = new Dimension();
		this.currentposition = new GameBoard.Point3D();
		this.previousposition = new GameBoard.Point3D();
		this.currentplayer = 0;
		//this.clearPlayers();
		this.canplace = new ArrayList<Boolean>();
			//since we don't have real player data, use dummy players
			this.canplace.add(new Boolean(true));
			this.canplace.add(new Boolean(true));
		//a list of positions for pieces, related to piecetypes
		//private ArrayList<GameBoard.Point3D> piecepositions;
		//a list of types for pieces, outer=xcoord inner1=ycoord inner2=board
		this.piecetypes = new ArrayList<ArrayList<ArrayList<ArrayList<String>>>>();
		//a list of owners for pieces, outer=xcoord inner1=ycoord inner2=board
		this.pieceowners = new ArrayList<ArrayList<ArrayList<ArrayList<Integer>>>>();
		//the type of the current piece we are looking at
		this.currentpiece = "";
		//a list of positions that represent the current piece's current move
		this.currentmove = new ArrayList<GameBoard.Point3D>();
		//movable pieces
		this.movablelocations = new ArrayList<GameBoard.Point3D>();
		this.movabletypes = new ArrayList<String>();
		//available moves
		this.availablemoves = new ArrayList<GameBoard.Point3D>();
		//board and player counts
		this.boardcount = 0;
		this.playercount = 0;
		
		//tell the Button panel to switch over to setup view
		try{
			((GameView)UIController.grabState("GameView")).gamebuttons.displayGameSetup();
		}catch (NullPointerException EXC){
			//GameView not instantiated yet
		}
		
	}
	
	//hidden method that adds image data to the property lists
	// name is the identifier for the image
	// fileloc is the system path to the image file
	// preferredscale is the scale this image should use
	//   relative to preferred component scale
	// preferredtranslation is the change of coordinates this image should use
	//   whenever it is drawn 
	// autoanimate is whether this image should be animated by the default animation system
	//   or wait for some other system (such as special replay animation)
	//   only significant if using a gif with multiple frames
	// animscale is the scale of animation frames length
	//   by mapping, resulting in duplicate frames or missing frames
	//   only significant if using a gif with multiple frames
	// returns boolean for success, usually dependent on File operations
	/*
		ArrayList<String> imagenames;
		ArrayList<String> imagelocs;
		ArrayList<Dimension> imagedims;
		ArrayList<Point> imagepos;
		ArrayList<BufferedImage> imagerefs;
	*/
	private boolean prepareImage(String name, String fileloc, 
						Dimension preferredscale, Point preferredtranslation,
						boolean autoanimate, double animscale) {
		
		boolean success = true;
		this.imagenames.add(name);
		this.imagelocs.add(fileloc);
		this.imagedims.add(preferredscale);
		this.imagepos.add(preferredtranslation);
		//if the image passed uses GIF extension
		// we will treat it as a series of layered images that must be acquired and animated
		String[] splits = fileloc.split("\\.");
		String extension = splits[splits.length-1].toLowerCase();
		if (extension.equals("gif")){
			try{
				//retrieve the first image reader registered to read GIFs
				ImageReader GIFReader = ImageIO.getImageReadersBySuffix("gif").next();
				//load the image file as an image stream
				GIFReader.setInput(ImageIO.createImageInputStream(new File(fileloc)));
				//build an iterator for input images that are read using default params
				Iterator<IIOImage> myframes = GIFReader.readAll(null);
				//store the images into the animate references for this image
				ArrayList<BufferedImage> framelist = new ArrayList<BufferedImage>();
				while (myframes.hasNext()){
					framelist.add((BufferedImage)(myframes.next().getRenderedImage()));
				}
				//map our frames according to the animation scale
				int newlength = (int)Math.ceil(animscale * framelist.size());
				//verify we need to do any mapping
				if (newlength != framelist.size()){
					ArrayList<BufferedImage> frametmps = new ArrayList<BufferedImage>(framelist);
					framelist = new ArrayList<BufferedImage>();
					for (int i=0; i<newlength; ++i){
						framelist.add(frametmps.get((int)Math.floor(i / animscale)));
					}
				}
				//add our list of frames to the animate list for this image
				this.animaterefs.add(framelist);
				//set our starting frame if we have multiple frames
				// or tag as inanimate (-1) if we only have 1 frame
				if (framelist.size() > 1){
					//in the exception that we don't want this image to animate automatically,
					// tag as manual-animate (-2)
					if (!autoanimate){
						this.currentframe.add(new Integer(-2));
					}else{
						this.currentframe.add(new Integer(0));
					}
				}else{
					this.currentframe.add(new Integer(-1));
				}
				this.imagerefs.add(framelist.get(0));
			}catch (IOException EXC){
				//we should create a default image that java can draw
				this.prepareDefaultImage(name, fileloc, preferredscale, preferredtranslation);
				System.out.println(EXC + "\n" + fileloc);
				success = false;
			}catch (IllegalArgumentException EXC){
				//we should create a default image that java can draw
				this.prepareDefaultImage(name, fileloc, preferredscale, preferredtranslation);
				System.out.println(EXC + "\n" + fileloc);
				success = false;
			}catch (IllegalStateException EXC){
				//we should create a default image that java can draw
				this.prepareDefaultImage(name, fileloc, preferredscale, preferredtranslation);
				System.out.println(EXC + "\n" + fileloc);
				success = false;
			}
		}
		/*else if (extension.equals("png")){
			try{
				//retrieve the first image reader registered to read GIFs
				ImageReader PNGReader = ImageIO.getImageReadersBySuffix("png").next();
				//load the image file as an image stream
				PNGReader.setInput(ImageIO.createImageInputStream(new File(fileloc)));
				//build an iterator for input images that are read using default params
				Iterator<IIOImage> myframes = PNGReader.readAll(null);
				//store the images into the animate references for this image
				ArrayList<BufferedImage> framelist = new ArrayList<BufferedImage>();
				while (myframes.hasNext()){
					framelist.add((BufferedImage)(myframes.next().getRenderedImage()));
				}
				//add our list of frames to the animate list for this image
				this.animaterefs.add(framelist);
				//set our starting frame if we have multiple frames
				// or tag as inanimate (-1) if we only have 1 frame
				if (framelist.size() > 1){
					//in the exception that we don't want this image to animate automatically,
					// tag as manual-animate (-2)
					if (!autoanimate){
						this.currentframe.add(new Integer(-2));
					}else{
						this.currentframe.add(new Integer(0));
					}
				}else{
					this.currentframe.add(new Integer(-1));
				}
				this.imagerefs.add(framelist.get(0));
			}catch (IOException EXC){
				//we should create a default image that java can draw
				this.prepareDefaultImage(name, fileloc, preferredscale, preferredtranslation);
				System.out.println(EXC + "\n" + fileloc);
				success = false;
			}catch (IllegalArgumentException EXC){
				//we should create a default image that java can draw
				this.prepareDefaultImage(name, fileloc, preferredscale, preferredtranslation);
				System.out.println(EXC + "\n" + fileloc);
				success = false;
			}catch (IllegalStateException EXC){
				//we should create a default image that java can draw
				this.prepareDefaultImage(name, fileloc, preferredscale, preferredtranslation);
				System.out.println(EXC + "\n" + fileloc);
				success = false;
			}
		}*/
		else{
			//image is not gif, load it like normal
			try{
				this.imagerefs.add(ImageIO.read(new File(fileloc)));
				//we are not using an animated image
				// so we should set the animate properties appropriately
				this.animaterefs.add(new ArrayList<BufferedImage>());
				this.currentframe.add(new Integer(-1));
			}catch (IOException EXC){
				//we should create a default image that java can draw
				this.prepareDefaultImage(name, fileloc, preferredscale, preferredtranslation);
				System.out.println(EXC + "\n" + fileloc);
				success = false;
			}catch (IllegalArgumentException EXC){
				//we should create a default image that java can draw
				this.prepareDefaultImage(name, fileloc, preferredscale, preferredtranslation);
				System.out.println(EXC + "\n" + fileloc);
				success = false;
			}
		}
		
		return success;
		
	}
	
	//hidden method to handle setting up a default image
	// and fixing any list imbalances that might have occured when prepareImage() failed
	private void prepareDefaultImage(String name, String fileloc, 
						Dimension preferredscale, Point preferredtranslation) {
		
		//check to see if our image property lists are the same size
		//at the same time, find the smallest
		int smallest = 0;
		boolean samesize = true;
		smallest = imagenames.size();
		if (imagelocs.size() != smallest){
			samesize = false;
			if (imagelocs.size() < smallest){
				smallest = imagelocs.size();
			}
		}
		if (imagedims.size() != smallest){
			samesize = false;
			if (imagedims.size() < smallest){
				smallest = imagedims.size();
			}
		}
		if (imagepos.size() != smallest){
			samesize = false;
			if (imagepos.size() < smallest){
				smallest = imagepos.size();
			}
		}
		if (imagerefs.size() != smallest){
			samesize = false;
			if (imagerefs.size() < smallest){
				smallest = imagerefs.size();
			}
		}
		if (animaterefs.size() != smallest){
			samesize = false;
			if (animaterefs.size() < smallest){
				smallest = animaterefs.size();
			}
		}
		if (currentframe.size() != smallest){
			samesize = false;
			if (currentframe.size() < smallest){
				smallest = currentframe.size();
			}
		}
		//if they are not the same size
		// then cut off the ends until they match the smallest size
		while (imagenames.size() != smallest){
			imagenames.remove(smallest);
		}
		while (imagelocs.size() != smallest){
			imagelocs.remove(smallest);
		}
		while (imagedims.size() != smallest){
			imagedims.remove(smallest);
		}
		while (imagepos.size() != smallest){
			imagepos.remove(smallest);
		}
		while (imagerefs.size() != smallest){
			imagerefs.remove(smallest);
		}
		while (animaterefs.size() != smallest){
			animaterefs.remove(smallest);
		}
		while (currentframe.size() != smallest){
			currentframe.remove(smallest);
		}
		//rebuild the image property lists for a default image
		imagenames.add(name);
		imagelocs.add(fileloc);
		imagedims.add(preferredscale);
		imagepos.add(preferredtranslation);
		imagerefs.add(new UIController.NotFoundImage(preferredscale.width, preferredscale.height));
		animaterefs.add(new ArrayList<BufferedImage>());
		currentframe.add(new Integer(-1));
		
	}
	
	//hidden method used to fix the order of pieces on a single spot
	// for proper drawing (safezones moved to end)
	//the spot will be the current position
	private void fixupPosition() {
		
		//declares
		String temptype;
		Integer tempowner;
		boolean fixed = false;
		GameBoard.Point3D spot = this.currentposition;
		ArrayList<String> types = this.piecetypes.get(spot.x).get(spot.y).get(spot.z);
		ArrayList<Integer> owners = this.pieceowners.get(spot.x).get(spot.y).get(spot.z);
		
		//loop until the list has been fixed
		while (!fixed){
			boolean hitsafe = false;
			int safeindex = 0;
			boolean badorder = false;
			int badindex = 0;
			//pass through the list to see if the order is ok
			order: for (int i=0; i<types.size(); ++i){
				if (types.get(i).equals("SafeZone")){
					//mark the last found safezone
					hitsafe = true;
					safeindex = i;
				}else{
					if (hitsafe){
						//we hit something after hitting a safezone
						//mark this bad order
						badorder = true;
						badindex = i;
						break order;
					}
				}
			}
			//check if we still ahve a bad order
			if (badorder){
				//rotate the out-of-order
				temptype = types.get(badindex);
				tempowner = owners.get(badindex);
				types.set(badindex, types.get(safeindex));
				owners.set(badindex, owners.get(safeindex));
				types.set(safeindex, temptype);
				owners.set(safeindex, tempowner);
			}else{
				fixed = true;
			}
		}
		
	}
	
	//hidden method used to scale a Dimension object
	// depending on the current scale
	private Dimension getScaleSize(Dimension size) {
		
		//declare something useful
		Dimension newsize = new Dimension();
		double scale = this.getScale();
		//scale
		newsize.width = (int)(size.width * scale);
		newsize.height = (int)(size.height * scale);
		//do not allow new dimension to contain 0
		if (newsize.width == 0){
			newsize.width = 1;
		}
		if (newsize.height == 0){
			newsize.height = 1;
		}
		
		return newsize;
		
	}
	
	//hidden method used to scale a Point object
	// depending on the current scale
	private Point getScalePosition(Point position) {
		
		//declare something useful
		Point newposition = new Point();
		double scale = this.getScale();
		//scale
		newposition.x = (int)(position.x * scale);
		newposition.y = (int)(position.y * scale);
		
		return newposition;
		
	}
	
	//hidden method used to determine what the correct scale of a board square
	//  should be depending on view mode and relative component scale
	//used for both drawing pieces and board tiles in the right size
	private Dimension getSquareSize() {
		
		//declare some stuff
		Rectangle boardarea = null;
		Dimension squaresize = null;
		
		//get the component's scale
		double scale = this.getScale();
		
		//check what view we are using
		if (this.getViewState() == GameBoard.BACK_VIEW){
			//we are looking at the backviewboards
			//keep in mind boardarea remains unscaled
			boardarea = this.backboardrect;
			squaresize = new Dimension((int)(scale * boardarea.width / this.boardsize.width),
									 (int)(scale * boardarea.width / this.boardsize.height));
		}else if (this.getViewState() == GameBoard.SIDE_VIEW){
			//we are looking at the sideviewboards
			//keep in mind boardarea remains unscaled
			boardarea = this.sideboardrect;
			squaresize = new Dimension((int)(scale * boardarea.height / this.boardsize.width),
									 (int)(scale * boardarea.height / this.boardsize.height));
		}
		//do not allow new dimension to contain 0
		if (squaresize.width == 0){
			squaresize.width = 1;
		}
		if (squaresize.height == 0){
			squaresize.height = 1;
		}
		
		return squaresize;
		
	}
	
	
	//hidden method used to unscale a pixel point
	// so that it can be compared against the preferred point and dimension
	// of a certain fixed image
	private Point unscalePixel(Point pixel) {
		
		Point unscaledpixel = new Point();
		double scale = this.getScale();
		unscaledpixel.x = (int)(pixel.x / scale);
		unscaledpixel.y = (int)(pixel.y / scale);
		
		return unscaledpixel;
		
	}
	
	//hidden method used to map a position on a particular board
	//  to the corresponding point on this panel
	//  depending on which board we are looking at
	//  and possibly in the future, what rotation
	// this should return a point based on the center of a board position
	//Used for determining where to draw pieces on a board
	private Point getPixelFromLocation(GameBoard.Point3D position) {
		
		//declare some common vars we will use throughout this method
		//defines the area of our board
		Rectangle boardarea = null;
		//define the standard size of a square on the board relative to preferred board size
		Dimension squaresize = null;
		//defines the relative scale we should be working in
		double scale = this.getScale();
		
		Point pixel = new Point();
		
		//check what board view we are using
		if (this.getViewState() == GameBoard.BACK_VIEW){
			//we are looking at the backviewboards
			//keep in mind boardarea remains unscaled
			boardarea = this.backboardrect;
			//keep in mind squaresize is scaled
			squaresize = this.getSquareSize();
			//multiply out by square size to determine our pixel relative to the board
			pixel.x = (int)Math.floor(position.x * (squaresize.width));
			pixel.y = (int)Math.floor(position.y * (squaresize.height));
			//zero out the pixel position so that
			// it is relative to the start of our component
			// instead of the start of our board
			pixel.translate((int)Math.floor(scale*(boardarea.x)),
				(int)Math.floor(scale*(boardarea.y)));
			//shift pixel in to the center of the square rather than the top left corner
			pixel.x = pixel.x + (int)Math.floor(squaresize.width/2.0);
			pixel.y = pixel.y + (int)Math.floor(squaresize.height/2.0);
		}else if (this.getViewState() == GameBoard.SIDE_VIEW){
			//we are looking at the sideviewboards
			//keep in mind boardarea remains unscaled
			boardarea = this.sideboardrect;
			//keep in mind squaresize is scaled
			squaresize = this.getSquareSize();
			//define the area of each board
			Rectangle leftarea = new Rectangle(0, 0, (int)Math.floor(boardarea.height * scale),
										(int)Math.floor(boardarea.height * scale));
			Rectangle rightarea = new Rectangle((int)Math.floor((boardarea.height + 10) * scale),
										 0, (int)Math.floor(boardarea.height * scale),
										(int)Math.floor(boardarea.height * scale));
			//check which board we are looking at
			//based on the z value
			if (position.z == 0){
				//we are looking at the left board
				//multiply out by square size to determine our pixel relative to the board
				pixel.x = (int)Math.floor((position.x) * (squaresize.width)) + leftarea.x;
				pixel.y = (int)Math.floor((position.y) * (squaresize.height)) + leftarea.y;
			}else if(position.z == 1){
				//we are looking at the right board
				//multiply out by square size to determine our pixel relative to the board
				pixel.x = (int)Math.floor((position.x) * (squaresize.width)) + rightarea.x;
				pixel.y = (int)Math.floor((position.y) * (squaresize.height)) + rightarea.y;
			}
			//zero out the pixel position so that
			// it is relative to the start of our component
			// instead of the start of our board
			pixel.translate((int)Math.floor(scale*(boardarea.x)),
				(int)Math.floor(scale*(boardarea.y)));
			//shift pixel in to the center of the square rather than the top left corner
			pixel.x = pixel.x + (int)Math.floor(squaresize.width/2.0);
			pixel.y = pixel.y + (int)Math.floor(squaresize.height/2.0);
		}
		
		//System.out.println("Location to Pixel:" + position + " -> " + pixel);
		
		return pixel;
		
	}
	
	//hidden method used to map a point on this panel
	//  to the corresponding position on the board
	//  depending on which board we are looking at
	//  and possibly in the future, what rotation
	//Used for deciphering mouse clicks
	private GameBoard.Point3D getLocationFromPixel(Point pixel) {
		
		//declare some common vars we will use throughout this method
		//defines the area of our board
		Rectangle boardarea = null;
		//define the standard size of a square on the board relative to preferred board size
		Dimension squaresize = null;
		//defines the relative scale we should be working in
		double scale = this.getScale();
		
		GameBoard.Point3D position = new GameBoard.Point3D(-1,-1,-1);
		
		//check what board view we are using
		if (this.getViewState() == GameBoard.BACK_VIEW){
			//we are looking at the backviewboards
			//keep in mind boardarea remains unscaled
			boardarea = this.backboardrect;
			//keep in mind squaresize is scaled
			squaresize = this.getSquareSize();
			//zero out the pixel position so that
			// it is relative to the start of our board
			// instead of the start of our component
			pixel.translate((int)Math.floor(0 - scale*(boardarea.x)),
				(int)Math.floor(0 - scale*(boardarea.y)));
			//define the area of our board
			boardarea = new Rectangle(0, 0, (int)Math.floor(boardarea.width * scale),
										(int)Math.floor(boardarea.height * scale));
			//ensure we are inside our board
			if (boardarea.contains(pixel)){
				//divide out by square size to determine our coordinates
				position.x = (int)Math.floor(pixel.x / (squaresize.width));
				position.y = (int)Math.floor(pixel.y / (squaresize.height));
				//System.out.println("SquareSize:" + squaresize);
				//System.out.println("(pixel.y / (squaresize.height)):" + (pixel.y / (squaresize.height)));
				//System.out.println("(int)(pixel.y / (squaresize.height)):" + (int)(pixel.y / (squaresize.height)));
				//System.out.println("(int)Math.floor(pixel.y / (squaresize.height)):" + (int)Math.floor(pixel.y / (squaresize.height)));
				//System.out.println("(int)Math.floor(pixel.y / ((double)squaresize.height)):" + (int)Math.floor(pixel.y / ((double)squaresize.height)));
				//check which board we are looking at to obtain the z coord
				position.z = this.currentboard;
			}
		}else if (this.getViewState() == GameBoard.SIDE_VIEW){
			//we are looking at the sideviewboards
			//keep in mind boardarea remains unscaled
			boardarea = this.sideboardrect;
			//keep in mind squaresize is scaled
			squaresize = this.getSquareSize();
			//zero out the pixel position so that
			// it is relative to the start of our board
			// instead of the start of our component
			pixel.translate((int)Math.floor(0 - scale*(boardarea.x)),
				(int)Math.floor(0 - scale*(boardarea.y)));
			//define the area of each board
			Rectangle leftarea = new Rectangle(0, 0, (int)Math.floor(boardarea.height * scale),
										(int)Math.floor(boardarea.height * scale));
			Rectangle rightarea = new Rectangle((int)Math.floor((boardarea.height + 10) * scale),
										 0, (int)Math.floor(boardarea.height * scale),
										(int)Math.floor(boardarea.height * scale));
			//check which board we are looking at
			//based on the bounds
			if (leftarea.contains(pixel)){
				//we are looking at the left board
				position.z = 0;
				//divide out by square size to determine our coordinates
				position.x = (int)Math.floor((pixel.x - leftarea.x) / (squaresize.width));
				position.y = (int)Math.floor((pixel.y - leftarea.y) / (squaresize.height));
			}else if(rightarea.contains(pixel)){
				//we are looking at the right board
				position.z = 1;
				//divide out by square size to determine our coordinates
				position.x = (int)Math.floor((pixel.x - rightarea.x) / (squaresize.width));
				position.y = (int)Math.floor((pixel.y - rightarea.y) / (squaresize.height));
			}
		}
		
		System.out.println("Pixel to Location:" + pixel + " -> " + position);
		
		return position;
		
	}
	
	//hidden method used to verify if a piece can be added
	private boolean addPiece() {
		
		//build a string that represents the piece we want to add
		//"PieceName,OwnerName,XCoord,YCoord, ZCoord"
		GameBoard.Point3D localpos = this.currentposition;
		String piecedata = this.currentpiece + ","
			+ this.playerlist.get(this.currentplayer) + ","
			+ localpos.x + "," + localpos.y + ","
			+ localpos.z;
		//ask UIController if we can place a piece here
		boolean placed = UIController.addPiece(piecedata);
		if (placed){
			//if the UIController confirms the placement
			// we should add the specified piece type and owner
			// to the piece lists
			this.piecetypes.get(this.currentposition.x).get(this.currentposition.y).get(this.currentposition.z).add(this.currentpiece);
			this.pieceowners.get(this.currentposition.x).get(this.currentposition.y).get(this.currentposition.z).add(new Integer(this.currentplayer));
			//fixup the spot we added to
			this.fixupPosition();
			//we should tell game buttons to update its piece counts for the current player to see
			//and at the same time set if the current player can place or not
			this.canplace.set(this.currentplayer, new Boolean(((GameView)UIController.grabState("GameView")).gamebuttons.getPieceCounts()));
			//we should rotate the current player if the piece was placed
			this.nextSetupTurn();
			//make sure we paint ourselves
			this.repaint();
			return true;
		}else{
			return false;
		}
		
	}
	
	//hidden method used to sync up board data in lower levels with the data in this level
	private void syncBoardData() {
		
		ArrayList<String> boarddata = null;
		GameBoard.Point3D temppnt = new GameBoard.Point3D();
		//reset our boards
		this.piecetypes = new ArrayList<ArrayList<ArrayList<ArrayList<String>>>>();
		this.pieceowners = new ArrayList<ArrayList<ArrayList<ArrayList<Integer>>>>();
		this.defineBoardStructure();
		
		//we have to get data one board at a time
		for (int b=0; b<this.boardcount; ++b){
			//get board b data from UI Controller
			boarddata = UIController.getBoard(b);
			//parse data to add to boards
			//"PieceName,OwnerName,XCoord,YCoord,ZCoord"
			for (int i=0; i<boarddata.size(); ++i){
				ArrayList<String> type = new ArrayList<String>();
				ArrayList<String> owner = new ArrayList<String>();
				ArrayList<Integer> coords = new ArrayList<Integer>();
				String piece = boarddata.get(i);
				UIController.convertStringToPiece(piece, type, owner, coords);
				temppnt = new GameBoard.Point3D(coords.get(0).intValue(), coords.get(1).intValue(), coords.get(2).intValue());
				//add piece type
				piecetypes.get(temppnt.x).get(temppnt.y).get(temppnt.z).add(type.get(0));
				//add piece owner
				pieceowners.get(temppnt.x).get(temppnt.y).get(temppnt.z).add(new Integer(this.playerlist.indexOf(owner.get(0))));
			}
		}
		//if we are in setup or unstarted, push ourselves into play mode
		if (this.getBoardState() == GameBoard.SETUP
			|| this.getBoardState() == GameBoard.UNSTARTED){
			//make sure we use the first player
			//this is awkward because we need to call next play turn which will call next player
			//we should set this player to the first player
			//then we should use previous player to cycle backwards 1
			this.currentplayer = this.firstplayer;
			this.previousPlayer();
			if (!this.nextPlayTurn()){
				return;
			}
			this.setBoardState(GameBoard.PLAY);
		}
		
	}
	
	//hidden method used to initialize board data structures
	// layer 1 array list of type ArrayList<ArrayList<ArrayList<Type>>> with x elements
	// layer 2 array list of type ArrayList<ArrayList<Type>> with y elements
	// layer 3 array list of type ArrayList<Type> with board (2) elements
	// layer 4 array list of type Type with no elements
	private void defineBoardStructure() {
		
		for (int x=0; x<this.boardsize.width; ++x){
			this.piecetypes.add(new ArrayList<ArrayList<ArrayList<String>>>());
			this.pieceowners.add(new ArrayList<ArrayList<ArrayList<Integer>>>());
			for (int y=0; y<this.boardsize.height; ++y){
				this.piecetypes.get(x).add(new ArrayList<ArrayList<String>>());
				this.pieceowners.get(x).add(new ArrayList<ArrayList<Integer>>());
				for (int z=0; z<this.boardcount; ++z){
					this.piecetypes.get(x).get(y).add(new ArrayList<String>());
					this.pieceowners.get(x).get(y).add(new ArrayList<Integer>());
				}
			}
		}
		
	}
	
	//hidden method to get the available moves for the current piece
	// returns false if no available moves are found
	private boolean getAvailableMoves() {
		
		System.out.println("Getting available moves");
		//build a String from the piece data
		//"PieceName,OwnerName,XCoord,YCoord,ZCoord"
		ArrayList<String> piecedata = new ArrayList<String>();
		//strange
		UIController.convertPieceToString(this.currentpiece, this.playerlist.get(this.currentplayer), 
			this.currentposition.x, this.currentposition.y, this.currentposition.z, piecedata);
		//get available move data
		ArrayList<String> movesdata = UIController.getAvailableMoves(piecedata.get(0));
		if (movesdata.isEmpty()){
			System.out.println("no available moves");
			//no moves are found, return false
			return false;
		}
		//otherwise, parse the data
		//"XCoord,YCoord,ZCoord"
		this.availablemoves = new ArrayList<GameBoard.Point3D>();
		String[] splits = null;
		GameBoard.Point3D temppnt = new GameBoard.Point3D();
		for (int i=0; i<movesdata.size(); ++i){
			splits = movesdata.get(i).split("\\,");
			try{
				temppnt.x = Integer.parseInt(splits[0]);
				temppnt.y = Integer.parseInt(splits[1]);
				temppnt.z = Integer.parseInt(splits[2]);
			}catch (NumberFormatException EXC){
				//bad format, we should leave in defaults
			}
			this.availablemoves.add(new GameBoard.Point3D(temppnt));
			System.out.println("Available move:" + temppnt);
		}
		return true;
		
	}
	
	//hidden method used to get the list of pieces that can move
	// returns false if no pieces can be moved
	private boolean getMovablePieces() {
		
		System.out.println("Getting movable pieces");
		//get movable pieces data
		ArrayList<String> piecesdata = UIController.getMovablePieces();
		if (piecesdata.isEmpty()){
			//no movable pieces are found, return false
			System.out.println("no movable pieces");
			return false;
		}
		//otherwise, parse the data
		//"PieceName,OwnerName,XCoord,YCoord,ZCoord"
		this.movablelocations = new ArrayList<GameBoard.Point3D>();
		this.movabletypes = new ArrayList<String>();
		String[] splits = null;
		GameBoard.Point3D temppnt = new GameBoard.Point3D();
		for (int i=0; i<piecesdata.size(); ++i){
			splits = piecesdata.get(i).split("\\,");
			try{
				temppnt.x = Integer.parseInt(splits[2]);
				temppnt.y = Integer.parseInt(splits[3]);
				temppnt.z = Integer.parseInt(splits[4]);
			}catch (NumberFormatException EXC){
				//bad format, we should leave in defaults
			}
			//add piece type
			movabletypes.add(splits[0]);
			//add piece locations
			movablelocations.add(new GameBoard.Point3D(temppnt));
			System.out.println("available move: " + temppnt + ", type:" + splits[0]);
		}
		return true;
		
	}
	
	//hidden method used to cycle the player turns during PLAY mode
	//returns false if the end of the game was hit,
	// helping other methods identify when they should jump out
	private boolean nextPlayTurn() {
		
		//switch player
		this.nextPlayer();
		//make sure moving flag is off
		this.movingpiece = false;
		//clear out list of available moves
		this.availablemoves = new ArrayList<GameBoard.Point3D>();
		//clear out current piece
		this.currentpiece = "";
		//clear out current and previous positions
		this.currentposition = new GameBoard.Point3D(0,0,this.currentboard);
		this.previousposition = this.currentposition;
		//clear out current move
		this.currentmove = new ArrayList<GameBoard.Point3D>();
		//get list of movable pieces
		boolean found = this.getMovablePieces();
		if (!found){
			//no movable pieces were found at the start of the turn
			//call end game procedure
			found = endGameCheck();
			if (found){
				return false;
			}
		}
		//the user should already know whose turn it it based on the added names and arrows
		//let the user know whose turn it is
		//this.showInfoNotice(this.playerlist.get(this.currentplayer) + ", it is your turn now.", "Switch Turn");
		UIController.printPopup(this.playerlist.get(this.currentplayer) + ", it is your turn to move...", 7.5);
		return true;
		
	}
	
	//hidden method used to cycle the player turns during PLACEMENT mode
	private void nextSetupTurn() {
		
		//switch player
		this.nextPlayer();
		//make sure moving flag is off
		this.movingpiece = false;
		//clear out current piece
		this.currentpiece = "";
		//clear out current and previous positions but keep track of board
		this.currentposition = new GameBoard.Point3D(0,0,this.currentboard);
		this.previousposition = this.currentposition;
		//we should tell game buttons to update its piece counts for the next player to see
		//and at the same time set if the next player can place or not
		this.canplace.set(this.currentplayer, new Boolean(((GameView)UIController.grabState("GameView")).gamebuttons.getPieceCounts()));
		System.out.println(this.playerlist.get(this.currentplayer) + " can still place more pieces:" + this.canplace.get(this.currentplayer));
		//we should check if there are no more players that can place pieces
		// this would signify the state change from placement to play
		boolean cannotplace = true;
		for (int i=0; i<this.canplace.size(); ++i){
			cannotplace = (cannotplace && !this.canplace.get(i).booleanValue());
		}
		if (cannotplace){
			System.out.println("Nobody can place a piece anymore, switch to play mode.");
			//let the user know board setup is finished
			/*
			this.showInfoNotice("Board setup is complete, it is time to play the game..."
				+ "\n" + this.playerlist.get(this.firstplayer) + ", you will make the first move!", "Game Play Started");
				*/
			UIController.printPopup("Board setup complete!"
				+ "<br>" + this.playerlist.get(this.firstplayer) + " will make the first move...", 11.5);
			//make sure we use the first player
			//this is awkward because we need to call next play turn which will call next player
			//we should set this player to the first player
			//then we should use previous player to cycle backwards 1
			this.currentplayer = this.firstplayer;
			this.previousPlayer();
			if (!this.nextPlayTurn()){
				return;
			}
			this.setBoardState(GameBoard.PLAY);
		}else{
			System.out.println("Someone can still place pieces.");
			//let the user know whose turn it is
			//this.showInfoNotice(this.playerlist.get(this.currentplayer) + ", it is your turn to place a piece now.", "Switch Turn");
			UIController.printPopup(this.playerlist.get(this.currentplayer) + ", it is your turn to place a piece...", 4.5);
		}
	}
	
	//hidden method to cycle to the next player
	private void nextPlayer() {
		
		this.currentplayer = (this.currentplayer + 1) % this.playercount;
		
	}
	//hidden method to cycle to the previous player
	private void previousPlayer() {
		
		this.currentplayer = Math.abs(this.currentplayer - 1) % this.playercount;
		
	}
	
	//hidden method to cycle to the next board
	private void nextBoard() {
		
		this.currentboard = (this.currentboard + 1) % this.boardcount;
		this.currentposition.z = this.currentboard;
		this.repaint();
		
	}
	//hidden method to cycle to the previous board
	private void previousBoard() {
		
		this.currentboard = (this.currentboard + 1) % this.boardcount;
		this.currentposition.z = this.currentboard;
		this.repaint();
		
	}
	
	
	//hidden method to verify the end of the game
	// asks UIController to see who has won the game
	// if UI returns empty, game is not over
	//returns true if it is indeed the end of the game
	private boolean endGameCheck() {
		
		//call UI Controller to see if this is the end of the game
		//for now, let's just assume that if this is called, the current player must be the loser
		
		//this.showInfoNotice(this.playerlist.get(this.currentplayer) + ", you have lost the game!!!!", "Game Over");
		//tell logic
		UIController.loseGame(this.playerlist.get(this.currentplayer));
		//check if user wants to start a new game right away
		//this.askNewGame();
		//jump to game review
		try{
			((GameReview)UIController.grabState("GameReview")).setPlayers(this.playerlist);
			((GameReview)UIController.grabState("GameReview")).setWinner(this.playerlist.get((this.currentplayer + 1) % this.playerlist.size()));
		}catch (NullPointerException EXC){
			System.out.println("Game Board failed to gain instance of GameReview");
		}
		//reset data since game has ended
		this.resetData();
		UIController.changeState("GameReview");
		return true;
		
	}
	
	//hidden method to be called under any end game circumstances to determine if the user wants to start a new game
	private void askNewGame() {
		
		
		//check is user wants to continue
		boolean newgame = this.askYNQuestion("Would you like to start a new game?"
			+ "\nIf not then game will close to main menu.", "New Game?");
		//reset data regardless of user response
		this.resetData();
		if (newgame){
			//make sure we tell game to reset data as well
			//UIController.newGame();
			//nvm, set game players will call new game
			//we should make sure the new game retains the players
			UIController.setGamePlayers(this.playerlist);
			//jump back into game
			UIController.changeState("GameView");
		}else{
			//jump out to main menu
			UIController.changeState("MainView");
		}
		
		
	}
	
	//hidden method used to select an existing piece for movement
	// piece selected based on the local position parameter
	// if piece cannot be select from this spot, return false
	private boolean selectPiece() {
		
		boolean found = false;
		GameBoard.Point3D temppos = null;
		int looper = 0;
		GameBoard.Point3D localpos = this.currentposition;
		
		System.out.println("Selecting piece from:" + localpos);
		
		//use our current list of movable pieces to determine if this location has one
		searchloc: for (looper=0; looper<this.movablelocations.size(); ++looper){
			temppos = this.movablelocations.get(looper);
			System.out.println("Movable:" + temppos);
			if (temppos.x == localpos.x 
				&& temppos.y == localpos.y && temppos.z == localpos.z){
				found = true;
				break searchloc;
			}
		}
		if (found){
			//the location clicked has movable pieces, let's check what type they are
			String piecedata = this.movabletypes.get(looper);
			//find the first board piece that matches the type
			found = false;
			searchtype: for (looper=0; 
				looper<this.piecetypes.get(localpos.x).get(localpos.y).get(localpos.z).size(); ++looper){
				if (this.piecetypes.get(localpos.x).get(localpos.y).get(localpos.z).get(looper).equals(piecedata)){
					System.out.println("this piece type matches the movable type");
					//make sure owners match up as well
					if (this.pieceowners.get(localpos.x).get(localpos.y).get(localpos.z).get(looper).intValue()
						 == this.currentplayer){
						System.out.println("this piece belongs to the current player");
						found = true;
						break searchtype;
					}
				}
			}
			//if we found a match
			if (found){
				System.out.println("this piece can be moved");
				this.currentpiece = this.piecetypes.get(localpos.x).get(localpos.y).get(localpos.z).get(looper);
				//get data for available moves if it is not yet avilable
				if ((this.availablemoves == null) || (this.availablemoves.isEmpty())){
					System.out.println("available moves data not found, regenerating");
					found = this.getAvailableMoves();
				}else{
					System.out.println("available moves data found, assuming it is valid");
					found = true;
				}
				if (found){
					
					//remove the found match from our board list and setup drawing parameters
					// for moving a piece
					this.currentpiece = this.piecetypes.get(localpos.x).get(localpos.y).get(localpos.z).remove(looper);
					this.pieceowners.get(localpos.x).get(localpos.y).get(localpos.z).remove(looper);
					this.previousposition = new GameBoard.Point3D(localpos);
					this.currentmove = new ArrayList<GameBoard.Point3D>();
					this.currentmove.add(this.previousposition);
					this.movingpiece = true;
					//make sure we paint ourselves
					this.repaint();
					return true;
				}else{
					//a piece claimed to be movable turns out to have no moves
					//if game logic does everything correct this shouldn't happen here, 
					// but nevertheless it signifies the end of the turn
					System.out.println("No available moves for movable piece, if you see this then something is wrong");
					if (!this.nextPlayTurn()){
						return true;
					}
				}
			}else{
				System.out.println("there should be a movable piece at this location, but it cannot be found");
				//something terribly wrong has happened
				// we can't seem to find the piece that was supposed to be here
				// we should sync up the boards
				this.syncBoardData();
				return false;
			}
		}else{
			//the location clicked has no movable pieces registered
			System.out.println("location clicked has no movable pieces registered");
			return false;
		}
		
		return true;
		
	}
	
	//hidden method used to verify if a piece can be moved
	private boolean movePiece() {
		
		System.out.println("moving piece");
		//declare some temps
		boolean valid = false;
		GameBoard.Point3D temppnt = null;
		GameBoard.Point3D localpos = null;
		String piecedata = "";
		ArrayList<Point> movecoords = null;
		ArrayList<Integer> moveboards = null;
		ArrayList<String> changedata = null;
		boolean moved = false;
		String[] splits = null;
		ArrayList<String> piecetype = new ArrayList<String>();
		ArrayList<String> pieceowner = new ArrayList<String>();
		ArrayList<Integer> piececoord = new ArrayList<Integer>();
		ArrayList<String> piecedatum = new ArrayList<String>();
		ArrayList<String> temptypes = null;
		ArrayList<Integer> tempowners = null;
		boolean found = false;
		boolean hitmine = false;
		boolean changetoking = false;
		int index = 0;
		
		//check if our current position is one of the available moves
		available: for (int i=0; i<this.availablemoves.size(); ++i){
			temppnt = this.availablemoves.get(i);
			if (temppnt.equals(this.currentposition)){
				System.out.println("position found in available moves");
				valid = true;
				break available;
			}
		}
		if (!valid){
			System.out.println("no available moves for the position");
			//this position is not available
			return false;
		}
		this.currentmove.add(this.currentposition);
		//build our move data
		// ArrayList<Point> coordinates
		// ArrayList<Integer> boards
		// String piece
		localpos = this.currentposition;
		//build a string that represents the piece we want to add
		// "PieceName,OwnerName,XCoord,YCoord, ZCoord"
		piecedata = "";
		piecedatum = new ArrayList<String>();
		piececoord.add(new Integer(localpos.x));
		piececoord.add(new Integer(localpos.y));
		piececoord.add(new Integer(localpos.z));
		UIController.convertPieceToString(this.currentpiece, this.playerlist.get(this.currentplayer),
			new Integer(localpos.x), new Integer(localpos.y), new Integer(localpos.z), piecedatum);
		/*
		piecedata = this.currentpiece + ","
			+ this.playerlist.get(this.currentplayer) + ","
			+ localpos.x + "," + localpos.y + ","
			+ localpos.z;
		*/
		//build a list of coordinates that represents the start and end position
		movecoords = new ArrayList<Point>();
		//build a list of boards that represents the start and end position
		moveboards = new ArrayList<Integer>();
		for (int i=0; i<currentmove.size(); ++i){
			movecoords.add(new Point(this.currentmove.get(i).x, this.currentmove.get(i).y));
			moveboards.add(new Integer(this.currentmove.get(i).z));
		}
		/*
		//check what changes need to be done to the board to show this move
		changedata = UIController.showMove(movecoords, moveboards, piecedata);
		if (changedata.size() == 0){
			System.out.println("show move reported no changes necessary");
			//no changes means something went wrong
			return false;
		}
		*/
		//tell UIController to move the piece
		ArrayList<String> removes = UIController.makeMove(movecoords, moveboards, piecedatum.get(0));
		//attempt to get the special flags
		if (removes.size() > 1){
			hitmine = Boolean.parseBoolean(removes.get(0));
			changetoking = Boolean.parseBoolean(removes.get(1));
		}
		//check if we didn't hit a mine
		if (!hitmine){
			//check if we didn't change into a king
			if (!changetoking){
				//add our piece at the end location of our move
				this.piecetypes.get(localpos.x).get(localpos.y).get(localpos.z).add(this.currentpiece);
			}else{
				//add a king at the end location of our move
				this.piecetypes.get(localpos.x).get(localpos.y).get(localpos.z).add("King");
			}
			//set our owner
			this.pieceowners.get(localpos.x).get(localpos.y).get(localpos.z).add(new Integer(this.currentplayer));
			this.fixupPosition();
		}else{
			//setup info to show mine explosion animation
			System.out.println("setting up explode animation during game play");
			this.animdims = new ArrayList<Dimension>();
			this.animpos = new ArrayList<Point>();
			Point tmppnt = new Point(movecoords.get(1));
			Dimension tmpdim = new Dimension();
			int tmpboard = moveboards.get(1);
			//define the positions and dimension changes
			this.animpos.add(this.getPixelFromLocation(new GameBoard.Point3D(tmppnt.x, tmppnt.y, tmpboard)));
			int tmpindex = this.imagenames.indexOf("Explosion");
			tmpdim = this.getSquareSize();
			this.animdims.add(new Dimension(tmpdim.width * 3, tmpdim.height * 3));
			this.animationstep = 0;
			this.animsteps = 18;
			this.animindex = tmpindex;
		}
		//remove pieces if any
		if (removes.size() > 2){
			System.out.println("There are pieces to remove...");
			//loop through the jumped pieces
			for (int i=2; i<removes.size(); ++i){
				System.out.println("Removing: " + removes.get(i));
				piececoord = new ArrayList<Integer>();
				piecetype = new ArrayList<String>();
				pieceowner = new ArrayList<String>();
				UIController.convertStringToPiece(removes.get(i), piecetype, pieceowner, piececoord);
				//get the pieces at this location
				temptypes = this.piecetypes.get(piececoord.get(0).intValue()).get(piececoord.get(1).intValue()).get(piececoord.get(2).intValue());
				tempowners = this.pieceowners.get(piececoord.get(0).intValue()).get(piececoord.get(1).intValue()).get(piececoord.get(2).intValue());
				//just for fun, make sure our type list and owner list are the same size
				if (temptypes.size() != tempowners.size()){
					System.out.println("board changes: owner list and piece type list are different sizes");
					//serious error, make sure we resync
					this.syncBoardData();
					return false;
				}
				//check if the specified piece is there
				found = false;
				System.out.println("GAMEBOARD: piece to remove = " + piecetype.get(0) + "," + pieceowner.get(0));
				findpiece: for (index=0; index<temptypes.size(); ++index){
					System.out.println("GAMEBOARD: piece[" + index + "] = " + temptypes.get(index) + "," + this.playerlist.get(tempowners.get(index).intValue()));
					if (temptypes.get(index).equals(piecetype.get(0)) && this.playerlist.get(tempowners.get(index).intValue()).equals(pieceowner.get(0))){
						found = true;
						break findpiece;
					}
				}
				//remove the piece if found
				if (found){
					System.out.println("...piece removed");
					this.piecetypes.get(piececoord.get(0).intValue()).get(piececoord.get(1).intValue()).get(piececoord.get(2).intValue()).remove(index);
					this.pieceowners.get(piececoord.get(0).intValue()).get(piececoord.get(1).intValue()).get(piececoord.get(2).intValue()).remove(index);
				}else{
					System.out.println("GAMEBOARD: piece to be removed was not found!");
				}
			}
		}
		/*
		if (!moved){
			System.out.println("move was denied");
			//somehow we were denied our move
			return false;
		}
		//if the UIController confirms the move
		// we should use the change data to update our board
		// "Add/Remove,BoardName,PieceName,OwnerName,Visibility,XCoord,YCoord"
		for (int i=0; i<changedata.size(); ++i){
			splits = changedata.get(i).split("\\,");
			//get piece data
			try{
				temppnt.x = Integer.parseInt(splits[5]);
				temppnt.y = Integer.parseInt(splits[6]);
				temppnt.z = Integer.parseInt(splits[1]);
			}catch (NumberFormatException EXC){}
			piecetype = splits[2];
			pieceowner = splits[3];
			//get the pieces at this location
			temptypes = this.piecetypes.get(temppnt.x).get(temppnt.y).get(temppnt.z);
			tempowners = this.pieceowners.get(temppnt.x).get(temppnt.y).get(temppnt.z);
			//just for fun, make sure our type list and owner list are the same size
			if (temptypes.size() != tempowners.size()){
				System.out.println("board changes: owner list and piece type list are different sizes");
				//serious error, make sure we resync
				this.syncBoardData();
				return false;
			}
			//check if the specified piece is there
			found = false;
			findpiece: for (index=0; index<temptypes.size(); ++index){
				if (temptypes.get(index).equals(piecetype) && playerlist.get(tempowners.get(index).intValue()).equals(pieceowner)){
					found = true;
					break findpiece;
				}
			}
			//check if we are adding or removing
			if (splits[0].equals("Add") && !found){
				this.piecetypes.get(temppnt.x).get(temppnt.y).get(temppnt.z).add(piecetype);
				this.pieceowners.get(temppnt.x).get(temppnt.y).get(temppnt.z).add(new Integer(playerlist.indexOf(pieceowner)));
			}else if (splits[0].equals("Remove") && found){
				this.piecetypes.get(temppnt.x).get(temppnt.y).get(temppnt.z).remove(index);
				this.pieceowners.get(temppnt.x).get(temppnt.y).get(temppnt.z).remove(index);
			}
		}
		*/
		//make sure we paint ourselves
		this.repaint();
		//we should set our movable list to the resulting position of the move that was made
		// to indicate that only the previously moved piece can be moved
		this.movablelocations = new ArrayList<GameBoard.Point3D>();
		this.movablelocations.add(new GameBoard.Point3D(currentmove.get(currentmove.size()-1)));
		this.movabletypes = new ArrayList<String>();
		this.movabletypes.add(this.currentpiece);
		//we shouldn't be moving a piece anymore
		this.movingpiece = false;
		//update our available moves for the current piece if we did not hit a mine
		if (!hitmine){
			found = this.getAvailableMoves();
		}
		//we shouldn't be looking at a piece anymore
		this.currentpiece = "";
		if (!found || hitmine){
			//no moves are available, next turn
			if (!this.nextPlayTurn()){
				return true;
			}
		}
		return true;
		
	}
	
	
	//overridden MouseListener methods
	public void mouseEntered(MouseEvent EVE) {}
	public void mouseExited(MouseEvent EVE) {}
	public void mousePressed(MouseEvent EVE) {}
	public void mouseClicked(MouseEvent EVE) {
		
		//if someone dragged and then released
		// then let's assume they intended to click where they released
		//make sure this does not fire when we click
		// to avoid multiple click instances
		/*
		System.out.println("...Released, Modifiers=" + EVE.getModifiers() + ", ID=" + EVE.getID());
		if (EVE.getModifiers() != MouseEvent.MOUSE_CLICKED){
			this.mouseClicked(EVE);
		}
		*/
		//nvm that, we change it so that every mouse release is treated as a click
		
	}
	public void mouseReleased(MouseEvent EVE) {
		
		//check if we should block this click
		if (this.hasclicked){
			System.out.println("click was blocked");
			return;
		}
		//otherwise, allow this click and block subsequent clicks
		this.hasclicked = true;
		System.out.println("click was allowed, blocking subsequent clicks");
		//every mouse release should be treated as a click
		//System.out.println("\nMouseClicked");
		//declare any temp vars here
		Point imgpnt = null;
		Dimension imgdim = null;
		Rectangle imgrect = null;
		Point preferred = null;
		String piecedata = "";
		GameBoard.Point3D temppos = null;
		int looper = 0;
		int index = 0;
		boolean placed = false;
		boolean found = false;
		
		//A mouse click is the only mouse action we care about for now
		//see where the click came from relative to this component
		Point source = EVE.getPoint();
		//make sure we are in a mode where we care about mouse clicks
		// such as PLACEMENT or PLAY modes
		
		//check for clicks that are not specific to the boards first
		if (this.getBoardState() == GameBoard.PLACEMENT
			|| this.getBoardState() == GameBoard.PLAY){
			//scale back our source point to compare
			// against preferred points and dimensions
			preferred = this.unscalePixel(source);
			System.out.println("Source:" + source + " || Preferred:" + preferred);
			//check if we are in the right view mode to see the arrows
			if (this.getViewState() == GameBoard.BACK_VIEW){
				//check previous arrow
				//create a faux Rectangle so that we can see if our unscaled point falls inside it
				index = this.imagenames.indexOf("PreviousBoardArrow");
				imgpnt = this.imagepos.get(index);
				imgdim = this.imagedims.get(index);
				imgrect = new Rectangle(imgpnt.x, imgpnt.y, imgdim.width, imgdim.height);
				//System.out.println("Previous Arrow:" + imgrect);
				if (imgrect.contains(preferred)){
					//we should cycle the boards backwards
					this.previousBoard();
					//we should jump out here because a click should never cause two actions
					this.hasclicked = false;
					System.out.println("clicks unblocked");
					return;
				}
				//check next arrow
				//create a faux Rectangle so that we can see if our unscaled point falls inside it
				index = this.imagenames.indexOf("NextBoardArrow");
				imgpnt = this.imagepos.get(index);
				imgdim = this.imagedims.get(index);
				imgrect = new Rectangle(imgpnt.x, imgpnt.y, imgdim.width, imgdim.height);
				//System.out.println("Next Arrow:" + imgrect);
				if (imgrect.contains(preferred)){
					//we should cycle the boards backwards
					this.nextBoard();
					//we should jump out here because a click should never cause two actions
					this.hasclicked = false;
					System.out.println("clicks unblocked");
					return;
				}
			}
		}
		
		//if we make it here, we are certain that the mouse click was related to the board
		
		// check if we were in PLACEMENT mode and moving (placing) a piece
		if (this.getBoardState() == GameBoard.PLACEMENT && this.movingpiece){
			//if we were placing a piece, we should check where
			temppos = this.getLocationFromPixel(source);
			//check if this position fits in our board size
			if (temppos.x >= 0 && temppos.x < boardsize.width
				&& temppos.y >= 0 && temppos.y < boardsize.height){
				this.currentposition = new GameBoard.Point3D(temppos);
				//try to add the current piece at the current location
				placed = this.addPiece();
				if (!placed){
					//we should let the user know the piece couldn't be added there
					/*
					this.showInfoNotice("The " + this.currentpiece + " piece cannot be placed.", 
						"Invalid location or no more pieces");
						*/
					UIController.printPopup("The piece cannot be placed there!", 5.5);
					//we should redraw to prevent frozen pieces left over on screen
					this.repaint();
				}
				//regardless, we should disable the moving flag after the user clicks on the board
				this.movingpiece = false;
				//we should jump out here because a click should never cause two actions
				this.hasclicked = false;
				System.out.println("clicks unblocked");
				return;
			}
		}
		// check if we were in PLAY mode
		if (this.getBoardState() == GameBoard.PLAY){
			//we should check where the user has clicked on the board
			temppos = this.getLocationFromPixel(source);
			//check if this position fits in our board size
			if (temppos.x >= 0 && temppos.x < boardsize.width
				&& temppos.y >= 0 && temppos.y < boardsize.height){
				this.currentposition = new GameBoard.Point3D(temppos);
				//if we are not moving a piece that means we wish to select a piece
				if (!this.movingpiece){
					//we are selecting a piece
					found = this.selectPiece();
					if (!found){
						//we should inform the user that there are no pieces at this location that can be moved
						/*
						this.showInfoNotice(this.playerlist.get(this.currentplayer) + ", the board location clicked"
							+ " does not contain any movable pieces.", 
							"No movable pieces");
							*/
						UIController.printPopup("No movable pieces at selected location!", 3.5);
						//we should jump out here because a click should never cause two actions
						this.hasclicked = false;
						System.out.println("clicks unblocked");
						return;
					}
					//keep this in focus
					this.requestFocusInWindow();
				}else{
					//we are moving a piece
					found = this.movePiece();
					if (!found){
						//we should inform the user that there are no pieces at this location that can be moved
						/*
						this.showInfoNotice(this.playerlist.get(this.currentplayer) 
							+ ", you cannot move to the board location clicked.",
							"Cannot move here");
							*/
						UIController.printPopup("Piece cannot be moved to selected location!", 3.5);
						//we should jump out here because a click should never cause two actions
						this.hasclicked = false;
						System.out.println("clicks unblocked");
						return;
					}
				}
				//we should jump out here because a click should never cause two actions
				this.hasclicked = false;
				System.out.println("clicks unblocked");
				return;
			}
		}
		
		//make sure we unblock the clicks if no valid paths were taken
		this.hasclicked = false;
		System.out.println("clicks unblocked");
		
	}
	
	//overridden MouseMotionListener methods
	public void mouseDragged(MouseEvent EVE) {
		
		//if someone dragged then let's assume they intended to click
		//this.mouseClicked(EVE);
		//no nvm that, causes mouse click to fire every time position changes while dragging
		//if someone dragged then let's assume they intended to be moving still
		this.mouseMoved(EVE);
		
	}
	public void mouseMoved(MouseEvent EVE) {
		
		//System.out.println("\nMouseMoved");
		//Mouse movement is the only action we care about for now
		//make sure we are in a mode where we care about mouse movements
		// such as PLACEMENT or PLAY modes
		if (this.getBoardState() == GameBoard.PLACEMENT
			|| this.getBoardState() == GameBoard.PLAY){
			//record the current mouse position
			this.mousepos = EVE.getPoint();
			//check if we are moving a piece and need to update the drawing
			if (this.movingpiece){
				//tell the component it should redraw itself
				this.repaint();
			}
			//keep this in focus
			this.requestFocusInWindow();
		}
		
	}
	
	//overridden ActionListener methods
	public void actionPerformed(ActionEvent EVE) {
		
		//we should expect an event fired by our timer
		//we should never have multiple calls to this from the same timer,
		// so stop the timer this came from
		if (EVE.getSource().equals(this.boardtimer)){
			this.boardtimer.stop();
		}
		//this should only be used when we are in placement, gameplay, replay or setup mode
		if (this.getBoardState() == GameBoard.SETUP && EVE.getSource().equals(this.boardtimer)){
			//remind us to do game and board setup
			this.boardtimer.stop();
			this.setupGame();
		}else if ((this.getBoardState() == GameBoard.PLACEMENT
			|| this.getBoardState() == GameBoard.PLAY)
			&& EVE.getSource().equals(this.boardtimer)){
			//animate all images not tagged as inanimate (-1) by cycling to the next frame
			boolean existing = false;
			for (int i=0; i<this.currentframe.size(); ++i){
				if (this.currentframe.get(i).intValue() > -1){
					existing = true;
					currentframe.set(i, new Integer((this.currentframe.get(i).intValue() + 1) % this.animaterefs.get(i).size()));
					this.imagerefs.set(i, this.animaterefs.get(i).get(this.currentframe.get(i)));
				}
			}
			//System.out.println("we are on step: " + this.animationstep + " out of " + this.animsteps + " steps");
			//check if we are done animating the current action
			if (this.animsteps < 0){
				//nothing to do here but sit still and look pretty!
			}else if (this.animsteps <= this.animationstep){
				System.out.println("we are done animating this set of steps");
				//reset animation flags
				this.animsteps = -1;
				this.animindex = -1;
			}else{
				//check if this step does not involve drawing an image (animindex < 0)
				if (this.animindex >= 0){
					//System.out.println("time to animate another step");
					//build the proper image representing the current animation
					//map animation steps to image frames
					//check if we need to find the next frame of an animated image
					System.out.println("animation index: " + animindex);
					if (this.currentframe.get(this.animindex).intValue() != -1){
						int framemapped = (int)Math.floor(this.animationstep * (this.animaterefs.get(this.animindex).size() / (double)this.animsteps));
						System.out.println("next frame of image should be: " + framemapped);
						this.imagerefs.set(this.animindex, this.animaterefs.get(this.animindex).get(framemapped));
					}else{
						//paintComponent will draw the image at the index
					}
					//map positions to animation steps
					double posinter = this.animationstep * ((this.animpos.size() - 1) / (double)this.animsteps);
					int posmappedlow = (int)Math.floor(posinter);
					int posmappedhigh = (int)Math.ceil(posinter);
					//System.out.println("map position index: " + posinter + " between " + posmappedlow + " and " + posmappedhigh);
					//interpolate step position if necessary
					if (posmappedlow != posmappedhigh){
						this.steppos.x = this.animpos.get(posmappedlow).x 
							+ (int)Math.floor((posinter - posmappedlow) 
							* ((this.animpos.get(posmappedhigh).x - this.animpos.get(posmappedlow).x) / (posmappedhigh - posmappedlow)));
						this.steppos.y = this.animpos.get(posmappedlow).y 
							+ (int)Math.floor((posinter - posmappedlow) 
							* ((this.animpos.get(posmappedhigh).y - this.animpos.get(posmappedlow).y) / (posmappedhigh - posmappedlow)));
					}else{
						this.steppos = new Point(this.animpos.get(posmappedlow));
					}
					System.out.println("this step drawing point: " + this.steppos);
					//map dimensions to animation steps
					double diminter = this.animationstep * ((this.animdims.size() - 1) / (double)this.animsteps);
					int dimmappedlow = (int)Math.floor(diminter);
					int dimmappedhigh = (int)Math.ceil(diminter);
					//System.out.println("map dimension index: " + diminter + " between " + dimmappedlow + " and " + dimmappedhigh);
					//interpolate step dimension if necessary
					if (dimmappedlow != dimmappedhigh){
						this.stepdim.width = this.animdims.get(dimmappedlow).width 
							+ (int)Math.floor((diminter - dimmappedlow) 
							* ((this.animdims.get(dimmappedhigh).width - this.animdims.get(dimmappedlow).width) / (dimmappedhigh - dimmappedlow)));
						this.stepdim.height = this.animdims.get(dimmappedlow).height 
							+ (int)Math.floor((diminter - dimmappedlow) 
							* ((this.animdims.get(dimmappedhigh).height - this.animdims.get(dimmappedlow).height) / (dimmappedhigh - dimmappedlow)));
					}else{
						this.stepdim = new Dimension(this.animdims.get(dimmappedlow));
					}
					System.out.println("this step drawing size: " + this.stepdim);
				}
				//increment our step
				++this.animationstep;
				existing = true;
			}
			if (existing){
				this.repaint();
			}
		}else if (this.getBoardState() == GameBoard.REPLAY){
			//TBD
			System.out.println("Timer fired, animating replays!");
			//animate our images by cycling to the next frame
			for (int i=0; i<this.currentframe.size(); ++i){
				if (this.currentframe.get(i).intValue() > -1){
					//System.out.println("found a generic image to animate: " + this.imagenames.get(i) + " at frame: " + this.currentframe.get(i));
					currentframe.set(i, new Integer((this.currentframe.get(i).intValue() + 1) % this.animaterefs.get(i).size()));
					this.imagerefs.set(i, this.animaterefs.get(i).get(this.currentframe.get(i)));
				}
			}
			System.out.println("we are on step: " + this.animationstep + " out of " + this.animsteps + " steps");
			//check if we are done animating the current action
			if (this.animsteps <= this.animationstep){
				System.out.println("we are done animating this set of steps");
				//check if we have additional actions
				if (this.replayactpos < this.replayactions.size()){
					System.out.println("go setup next action");
					//setup next action
					try{
						this.nextAction();
					}catch (Exception EXC){
						System.out.println("Replays crashed most likely because of a fault in the data it received...");
						System.out.println(EXC + "\n");
						EXC.printStackTrace();
						//since we are in the middle of replays, we should try and stop replays
						//stop replay
						this.setBoardState(GameBoard.PLAY);
						//update button
						((GameView)UIController.grabState("GameView")).gamebuttons.updateShowReplay(false);
						//start up timer
						this.boardtimer.start();
						//let the user know we had a hiccup
						/*
						this.showInfoNotice("The replay has encountered a problem!"
							+ "\nThis was most likely caused by corrupted replay data.",
							"Replay Failure");
							*/
						UIController.printPopup("Replays has encountered a problem!", 12.5);
						return;
					}
				}else{
					System.out.println("go build next move/turn actions");
					//setup next turn
					try{
						this.buildActions();
					}catch (Exception EXC){
						System.out.println("Replays crashed most likely because of a fault in the data it received...");
						System.out.println(EXC + "\n");
						EXC.printStackTrace();
						//since we are in the middle of replays, we should try and stop replays
						//stop replay
						this.setBoardState(GameBoard.PLAY);
						//update button
						((GameView)UIController.grabState("GameView")).gamebuttons.updateShowReplay(false);
						//start up timer
						this.boardtimer.start();
						//let the user know we had a hiccup
						/*
						this.showInfoNotice("The replay has encountered a problem!"
							+ "\nThis was most likely caused by corrupted replay data.",
							"Replay Failure");
							*/
						UIController.printPopup("Replays has encountered a problem!", 12.5);
						return;
					}
				}
			}else{
				//check if this step does not involve drawing an image (animindex < 0)
				if (this.animindex >= 0){
					//System.out.println("time to animate another step");
					//build the proper image representing the current animation
					//map animation steps to image frames
					//check if we need to find the next frame of an animated image
					System.out.println("animation index: " + animindex);
					if (this.currentframe.get(this.animindex).intValue() != -1){
						int framemapped = (int)Math.floor(this.animationstep * (this.animaterefs.get(this.animindex).size() / (double)this.animsteps));
						System.out.println("next frame of image should be: " + framemapped);
						this.imagerefs.set(this.animindex, this.animaterefs.get(this.animindex).get(framemapped));
					}else{
						//paintComponent will draw the image at the index
					}
					//map positions to animation steps
					double posinter = this.animationstep * ((this.animpos.size() - 1) / (double)this.animsteps);
					int posmappedlow = (int)Math.floor(posinter);
					int posmappedhigh = (int)Math.ceil(posinter);
					//System.out.println("map position index: " + posinter + " between " + posmappedlow + " and " + posmappedhigh);
					//interpolate step position if necessary
					if (posmappedlow != posmappedhigh){
						this.steppos.x = this.animpos.get(posmappedlow).x 
							+ (int)Math.floor((posinter - posmappedlow) 
							* ((this.animpos.get(posmappedhigh).x - this.animpos.get(posmappedlow).x) / (posmappedhigh - posmappedlow)));
						this.steppos.y = this.animpos.get(posmappedlow).y 
							+ (int)Math.floor((posinter - posmappedlow) 
							* ((this.animpos.get(posmappedhigh).y - this.animpos.get(posmappedlow).y) / (posmappedhigh - posmappedlow)));
					}else{
						this.steppos = new Point(this.animpos.get(posmappedlow));
					}
					System.out.println("this step drawing point: " + this.steppos);
					//map dimensions to animation steps
					double diminter = this.animationstep * ((this.animdims.size() - 1) / (double)this.animsteps);
					int dimmappedlow = (int)Math.floor(diminter);
					int dimmappedhigh = (int)Math.ceil(diminter);
					//System.out.println("map dimension index: " + diminter + " between " + dimmappedlow + " and " + dimmappedhigh);
					//interpolate step dimension if necessary
					if (dimmappedlow != dimmappedhigh){
						this.stepdim.width = this.animdims.get(dimmappedlow).width 
							+ (int)Math.floor((diminter - dimmappedlow) 
							* ((this.animdims.get(dimmappedhigh).width - this.animdims.get(dimmappedlow).width) / (dimmappedhigh - dimmappedlow)));
						this.stepdim.height = this.animdims.get(dimmappedlow).height 
							+ (int)Math.floor((diminter - dimmappedlow) 
							* ((this.animdims.get(dimmappedhigh).height - this.animdims.get(dimmappedlow).height) / (dimmappedhigh - dimmappedlow)));
					}else{
						this.stepdim = new Dimension(this.animdims.get(dimmappedlow));
					}
					System.out.println("this step drawing size: " + this.stepdim);
				}
				//increment our step
				++this.animationstep;
				this.repaint();
			}
		}
		//start the timer we stopped in the beginning back up
		if (EVE.getSource().equals(this.boardtimer)){
			this.boardtimer.start();
		}
		
	}
	
	//hidden method to obtain the initial board state and list of moves from Logic layer
	// then store them into lists
	private void getReplayData(int gobackmoves) {
		
		//make sure replays are off while we are doing all the dirty work
		this.boardtimer.stop();
		
		//define some variables to help us with storage
		String[] splits = null;
		GameBoard.Point3D tmppnt = null;
		
		System.out.println("getting replay data...");
		//allow us to dynamically check if we can use existing replay data
		// and build on top of it rather than starting over completely
		//scratch that, it is a very unlikely event
		/*
		int existingturns = 0;
		if (this.replaymoves != null){
			existingturns = this.replaymoves.size()-1;
			System.out.println("using existing replay data of size: " + existingturns);
		}
		*/
		this.replaymoves = UIController.getReplayMoves();
		System.out.println("replay moves data acquired with size: " + this.replaymoves.size());
		/*
		if ((this.replaymoves.size() - gobackmoves) <= existingturns){
			existingturns = 0;
			System.out.println("user wants to go back further than current replay data, cannot use existing replay board states");
		}
		*/
		
		//if (existingturns <= 0){
			System.out.println("rebuilding replay boards structure");
			//acquire board data
			ArrayList<String> boarddata = UIController.getReplayBoards();
			System.out.println("replay boards data acquired with size: " + boarddata.size());
			//setup replay boards
			this.replaytypes = new ArrayList<ArrayList<ArrayList<ArrayList<String>>>>();
			this.replayowners = new ArrayList<ArrayList<ArrayList<ArrayList<Integer>>>>();
			for (int x=0; x<this.boardsize.width; ++x){
				this.replaytypes.add(new ArrayList<ArrayList<ArrayList<String>>>());
				this.replayowners.add(new ArrayList<ArrayList<ArrayList<Integer>>>());
				for (int y=0; y<this.boardsize.height; ++y){
					this.replaytypes.get(x).add(new ArrayList<ArrayList<String>>());
					this.replayowners.get(x).add(new ArrayList<ArrayList<Integer>>());
					for (int z=0; z<this.boardcount; ++z){
						this.replaytypes.get(x).get(y).add(new ArrayList<String>());
						this.replayowners.get(x).get(y).add(new ArrayList<Integer>());
					}
				}
			}
			//build initial board states
			splits = null;
			tmppnt = new GameBoard.Point3D(0,0,0);
			String tmptype = "";
			int tmpowner = 0;
			//loop through data pieces
			for (int i=0; i<boarddata.size(); ++i){
				//"PieceName,OwnerName,XCoord,YCoord,ZCoord"
				System.out.println("adding piece data to the replay boards: " + boarddata.get(i));
				splits = boarddata.get(i).split("\\,");
				tmptype = splits[0];
				tmpowner = this.playerlist.indexOf(splits[1]);
				try{
					tmppnt.x = Integer.parseInt(splits[2]);
					tmppnt.y = Integer.parseInt(splits[3]);
					tmppnt.z = Integer.parseInt(splits[4]);
				}catch (NumberFormatException EXC){}
				//add data to the replay board
				this.replaytypes.get(tmppnt.x).get(tmppnt.y).get(tmppnt.z).add(tmptype);
				this.replayowners.get(tmppnt.x).get(tmppnt.y).get(tmppnt.z).add(new Integer(tmpowner));
			}
		//}else{
		//	//we should already have an existing board state
		//	System.out.println("using existing replay boards structure and data");
		//}
		
		//acquire move data
		/*
		{ "mine_hit", 
			"change_to_king", 
			"initial_position", 
			["jumped_position", ... ,] 
			"destination_position" }
		*/
		/*
		ArrayList<ArrayList<String>> movedata = UIController.getReplayMoves();
		//split move data into turns
		String tmpmover = "";
		String prevmover = "";
		int movestart = 0;
		for (int movecurr=0; movecurr<movedata.size(); ++movecurr){
			//obtain the player making the current move segment
			//verify format
			if (movedata.get(movecurr).size() >= 4){
				//extract player name
				//splits = movedata.get
				//hold that thought, possible format change ahead
			}else{
				System.out.println("Invalid replay moves data");
			}
		}
		*/
		
		//Just in case the user tries to go back more moves than are available...
		// verify we do not go negative for starting range
		this.replaymovepos = 0;
		if (replaymoves.size() >= gobackmoves){
			this.replaymovepos = (replaymoves.size() - gobackmoves);
			System.out.println("user wants to go back a valid range, good");
		}
		System.out.println("starting replay from turn: " + this.replaymovepos);
		//advance our board state to match the starting move position
		boolean hitmine = false;
		boolean changetoking = false;
		int moveowner = this.firstplayer;
		ArrayList<GameBoard.Point3D> points = null;
		tmppnt = null;
		//String tmptype = "";
		tmptype = "";
		//loop through each turn until we hit the replay start
		loopturns: for (int turn=0; turn<this.replaymovepos; ++turn){
			System.out.println("Building turn: " + turn);
			//jump ahead to avoid recalculating if possible
			//too rare of an event, nvm that
			/*
			if (existingturns > turn){
				turn = turn + existingturns;
				moveowner = (moveowner + existingturns) % this.playerlist.size();
			}
			*/
			//loop through each move in the turn
			loopmoves: for (int mov=0; mov<this.replaymoves.get(turn).size(); ++mov){
				System.out.println("Building move: " + mov);
				//verify the format
				/*
				{ "mine_hit", 
					"change_to_king", 
					"initial_position", 
					["jumped_position", ... ,] 
					"destination_position" }
				*/
				if (this.replaymoves.get(turn).get(mov).size() < 4){
					System.out.println("invalid move format while building replay, less than 4 properties found");
				}
				//build move parameters
				points = new ArrayList<GameBoard.Point3D>();
				hitmine = Boolean.parseBoolean(this.replaymoves.get(turn).get(mov).get(0));
				changetoking = Boolean.parseBoolean(this.replaymoves.get(turn).get(mov).get(1));
				looppoints: for (int ploc=2; ploc<this.replaymoves.get(turn).get(mov).size(); ++ploc){
					tmppnt = new GameBoard.Point3D(0,0,0);
					try{
						System.out.println("parsing move point: " + this.replaymoves.get(turn).get(mov).get(ploc));
						splits = this.replaymoves.get(turn).get(mov).get(ploc).split("\\,");
						if (splits.length < 3){
							throw new NumberFormatException();
						}
						tmppnt.x = Integer.parseInt(splits[0]);
						tmppnt.y = Integer.parseInt(splits[1]);
						tmppnt.z = Integer.parseInt(splits[2]);
					}catch (NumberFormatException EXC){
						System.out.println("invalid location format while building replay");
					}
					points.add(tmppnt);
				}
				/*
				//on our first pass, we should verify the first player
				if (turn == 0 && mov == 0){
					tmppnt = points.get(0);
					findowner: for (int j=0; j<replaytypes.get(tmppnt.x).get(tmppnt.y).get(tmppnt.z).size(); ++j){
						if (replaytypes.get(tmppnt.x).get(tmppnt.y).get(tmppnt.z).
					}
					moveowner = replaytypes.get(tmppnt.x).get(tmppnt.y).get(tmppnt.z).indexOf("Checker");
					if (moveowner > -1){
						moveowner = replayowners.get(tmppnt.x).get(tmppnt.y).get(tmppnt.z).get(moveowner).intValue();
					}else{
						moveowner = replaytypes.get(tmppnt.x).get(tmppnt.y).get(tmppnt.z).indexOf("King");
						if (moveowner > -1){
							
						}
					}
					if (moveowner != this.firstplayer){
						System.out.println()
					}
				}
				*/
				//make the board changes
				//remove piece from starting location
				tmptype = "";
				tmppnt = points.get(0);
				findpiece: for (int j=0; j<replayowners.get(tmppnt.x).get(tmppnt.y).get(tmppnt.z).size(); ++j){
					if ((replaytypes.get(tmppnt.x).get(tmppnt.y).get(tmppnt.z).get(j).equals("Checker")
						|| replaytypes.get(tmppnt.x).get(tmppnt.y).get(tmppnt.z).get(j).equals("King"))
						&& (replayowners.get(tmppnt.x).get(tmppnt.y).get(tmppnt.z).get(j).equals(new Integer(moveowner)))){
						tmptype = replaytypes.get(tmppnt.x).get(tmppnt.y).get(tmppnt.z).get(j);
						replaytypes.get(tmppnt.x).get(tmppnt.y).get(tmppnt.z).remove(j);
						replayowners.get(tmppnt.x).get(tmppnt.y).get(tmppnt.z).remove(j);
						System.out.println("starting piece found, removing: " + tmptype);
						break findpiece;
					}
				}
				//add piece to the ending location
				tmppnt = points.get(points.size()-1);
				//only add to destination if we did not hit a mine
				if (!hitmine){
					replayowners.get(tmppnt.x).get(tmppnt.y).get(tmppnt.z).add(new Integer(moveowner));
					if (changetoking){
						replaytypes.get(tmppnt.x).get(tmppnt.y).get(tmppnt.z).add("King");
						System.out.println("adding starting piece to ending location, change to king flag set");
					}else{
						replaytypes.get(tmppnt.x).get(tmppnt.y).get(tmppnt.z).add(tmptype);
						System.out.println("adding starting piece to ending location, piece type: " + tmptype);
					}
				}
				//remove smartmines if necessary
				if (hitmine){
					System.out.println("smart mine hit");
					tmppnt = points.get(points.size()-1);
					findpiece: for (int j=0; j<replayowners.get(tmppnt.x).get(tmppnt.y).get(tmppnt.z).size(); ++j){
						if ((replaytypes.get(tmppnt.x).get(tmppnt.y).get(tmppnt.z).get(j).equals("Mine"))
							&& (!replayowners.get(tmppnt.x).get(tmppnt.y).get(tmppnt.z).get(j).equals(new Integer(moveowner)))){
							replaytypes.get(tmppnt.x).get(tmppnt.y).get(tmppnt.z).remove(j);
							replayowners.get(tmppnt.x).get(tmppnt.y).get(tmppnt.z).remove(j);
							System.out.println("Smart mine found, removing");
						}
					}
					//the following is redundant, we should just prevent the piece from being added in the first place
					/*
					findpiece: for (int j=0; j<replayowners.get(tmppnt.x).get(tmppnt.y).get(tmppnt.z).size(); ++j){
						if ((replaytypes.get(tmppnt.x).get(tmppnt.y).get(tmppnt.z).get(j).equals("Checker")
							|| replaytypes.get(tmppnt.x).get(tmppnt.y).get(tmppnt.z).get(j).equals("King"))
							&& (replayowners.get(tmppnt.x).get(tmppnt.y).get(tmppnt.z).get(j).equals(new Integer(moveowner)))){
							replaytypes.get(tmppnt.x).get(tmppnt.y).get(tmppnt.z).remove(j);
							replayowners.get(tmppnt.x).get(tmppnt.y).get(tmppnt.z).remove(j);
							System.out.println("exploded piece found, removing");
						}
					}
					*/
				}
				//remove other pieces if found
				loopremoves: for (int k=1; k<points.size()-1; ++k){
					System.out.println("removing jumped piece: " + points.get(k));
					tmppnt = points.get(k);
					findpiece: for (int j=0; j<replayowners.get(tmppnt.x).get(tmppnt.y).get(tmppnt.z).size(); ++j){
						if ((replaytypes.get(tmppnt.x).get(tmppnt.y).get(tmppnt.z).get(j).equals("Checker")
							|| replaytypes.get(tmppnt.x).get(tmppnt.y).get(tmppnt.z).get(j).equals("King"))
							&& (!replayowners.get(tmppnt.x).get(tmppnt.y).get(tmppnt.z).get(j).equals(new Integer(moveowner)))){
							replaytypes.get(tmppnt.x).get(tmppnt.y).get(tmppnt.z).remove(j);
							replayowners.get(tmppnt.x).get(tmppnt.y).get(tmppnt.z).remove(j);
							System.out.println("jumped piece found, removing");
							break findpiece;
						}
					}
				}
			}
			//we should now be done doing the current turn
			moveowner = (moveowner + 1) % this.playerlist.size();
			System.out.println("done with turn, next turn belongs to: " + moveowner);
		}
		//we should now be at replay start
		this.replayowner = moveowner;
		System.out.println("done building initial replay, next turn belongs to: " + this.replayowner);
		//setup next turn
		//this.replaymovepos;
		this.replaymov = -1;
		this.buildActions();
		
	}
	
	//hidden controller method used to build the next set of actions required to animate the current move
	// the actions will be stored in a class variable for the animation timer and the painter
	private void buildActions() {
		
		//stop replays while we calculate stuff
		this.boardtimer.stop();
		
		//define common vars to be used
		GameBoard.Point3D tmppnt = null;
		ArrayList<GameBoard.Point3D> points = null;
		String action = "";
		int pindex = 0;
		boolean hitmine = false;
		boolean changetoking = false;
		String[] splits = null;
		int moveowner = 0;
		String tmptype = "";
		
		//check if we need to cycle to next move or over to next turn
		if ((this.replaymov + 1) >= this.replaymoves.get(this.replaymovepos).size()){
			++this.replaymovepos;
			this.replaymov = 0;
		}else{
			++this.replaymov;
		}
		this.replayowner = (this.replaymovepos + this.firstplayer) % this.playerlist.size();
		moveowner = this.replayowner;
		System.out.println("building actions for turn: " + this.replaymovepos + ", move: " + this.replaymov);
		//check if we are done animating
		if (this.replaymovepos >= this.replaymoves.size()){
			System.out.println("done animating");
			//pause animation
			this.boardtimer.stop();
			//inform that replay is over
			/*
			this.showInfoNotice("The replay has finished.",
				"No more turns to replay.");
				*/
			UIController.printPopup("The replay finished...", 7.5);
			//stop replay
			this.setBoardState(GameBoard.PLAY);
			//update button
			((GameView)UIController.grabState("GameView")).gamebuttons.updateShowReplay(false);
			//start animation
			this.boardtimer.start();
			return;
		}
		//reset our actions
		this.replayactions = new ArrayList<String>();
		//loop through each move in the turn
		int mov = this.replaymov;
		
			System.out.println("building move: " + mov);
			//build move parameters
			points = new ArrayList<GameBoard.Point3D>();
			hitmine = Boolean.parseBoolean(this.replaymoves.get(this.replaymovepos).get(mov).get(0));
			changetoking = Boolean.parseBoolean(this.replaymoves.get(this.replaymovepos).get(mov).get(1));
			looppoints: for (int ploc=2; ploc<this.replaymoves.get(this.replaymovepos).get(mov).size(); ++ploc){
				tmppnt = new GameBoard.Point3D(0,0,0);
				try{
					System.out.println("building point: " + this.replaymoves.get(this.replaymovepos).get(mov).get(ploc));
					splits = this.replaymoves.get(this.replaymovepos).get(mov).get(ploc).split("\\,");
					if (splits.length < 3){
						throw new NumberFormatException();
					}
					tmppnt.x = Integer.parseInt(splits[0]);
					tmppnt.y = Integer.parseInt(splits[1]);
					tmppnt.z = Integer.parseInt(splits[2]);
				}catch (NumberFormatException EXC){
					System.out.println("invalid location format while building replay");
				}
				points.add(tmppnt);
			}
			//find starting piece
			tmppnt = points.get(0);
			findpiece: for (pindex = 0; pindex<replayowners.get(tmppnt.x).get(tmppnt.y).get(tmppnt.z).size(); ++pindex){
				System.out.println("looking at " + tmppnt + " type: " 
					+ this.replaytypes.get(tmppnt.x).get(tmppnt.y).get(tmppnt.z).get(pindex)
					+ " owner: " + this.replayowners.get(tmppnt.x).get(tmppnt.y).get(tmppnt.z).get(pindex));
				if ((this.replaytypes.get(tmppnt.x).get(tmppnt.y).get(tmppnt.z).get(pindex).equals("Checker")
					|| this.replaytypes.get(tmppnt.x).get(tmppnt.y).get(tmppnt.z).get(pindex).equals("King"))
					&& (this.replayowners.get(tmppnt.x).get(tmppnt.y).get(tmppnt.z).get(pindex).equals(new Integer(moveowner)))){
					break findpiece;
				}
			}
			action = "Remove," + tmppnt.x + "," + tmppnt.y + "," + tmppnt.z + "," + pindex;
			System.out.println("found starting piece, action: " + action);
			this.replayactions.add(action);
			tmptype = this.replaytypes.get(tmppnt.x).get(tmppnt.y).get(tmppnt.z).get(pindex);
			//verify the piece image based on the current owner
			if (moveowner == 0){
				tmptype = "Red" + tmptype;
			}else{
				tmptype = "Black" + tmptype;
			}
			//find location to move piece
			tmppnt = points.get(points.size()-1);
			action = "Move," + tmptype + ","
				+ points.get(0).x + "," + points.get(0).y + "," + points.get(0).z + ","
				+ tmppnt.x + "," + tmppnt.y + "," + tmppnt.z;
			System.out.println("moving starting piece, action: " + action);
			this.replayactions.add(action);
			//remove color information from piece type
			if (tmptype.indexOf("King") > -1){
				tmptype = "King";
			}else{
				tmptype = "Checker";
			}
			//don't add piece to destination if we hit a mine
			if (!hitmine){
				if (changetoking){
					action = "Add,King," + tmppnt.x + "," + tmppnt.y + "," + tmppnt.z;
				}else{
					action = "Add," + tmptype + ","
						+ tmppnt.x + "," + tmppnt.y + "," + tmppnt.z;;
				}
				System.out.println("placing starting piece, action: " + action);
				this.replayactions.add(action);
			}
			//find pieces to destroy
			loopremoves: for (int k=1; k<points.size()-1; ++k){
				System.out.println("removing piece at point: " + points.get(k));
				tmppnt = points.get(k);
				findpiece: for (pindex = 0; pindex<replayowners.get(tmppnt.x).get(tmppnt.y).get(tmppnt.z).size(); ++pindex){
					if ((replaytypes.get(tmppnt.x).get(tmppnt.y).get(tmppnt.z).get(pindex).equals("Checker")
						|| replaytypes.get(tmppnt.x).get(tmppnt.y).get(tmppnt.z).get(pindex).equals("King"))
						&& (!replayowners.get(tmppnt.x).get(tmppnt.y).get(tmppnt.z).get(pindex).equals(new Integer(moveowner)))){
						break findpiece;
					}
				}
				action = "Destroy," + tmppnt.x + "," + tmppnt.y + "," + tmppnt.z;
				System.out.println("found piece to destroy, action: " + action);
				this.replayactions.add(action);
				action = "Remove," + tmppnt.x + "," + tmppnt.y + "," + tmppnt.z + "," + pindex;
				System.out.println("found piece to remove, action: " + action);
				this.replayactions.add(action);
			}
			//find smartmines to explode
			if (hitmine){
				System.out.println("removing mine");
				tmppnt = points.get(points.size()-1);
				findpiece: for (int j=0; j<replayowners.get(tmppnt.x).get(tmppnt.y).get(tmppnt.z).size(); ++j){
					if ((replaytypes.get(tmppnt.x).get(tmppnt.y).get(tmppnt.z).get(j).equals("Mine"))
						&& (!replayowners.get(tmppnt.x).get(tmppnt.y).get(tmppnt.z).get(j).equals(new Integer(moveowner)))){
						action = "Explode," + tmppnt.x + "," + tmppnt.y + "," + tmppnt.z;
						System.out.println("found mine to explode, action: " + action);
						this.replayactions.add(action);
						action = "Remove," + tmppnt.x + "," + tmppnt.y + "," + tmppnt.z + "," + j;
						System.out.println("found mine to remove, action: " + action);
						this.replayactions.add(action);
					}
				}
				//the following won't work because the piece has not been added to the board yet!
				/*
				findpiece: for (int j=0; j<replayowners.get(tmppnt.x).get(tmppnt.y).get(tmppnt.z).size(); ++j){
					if ((replaytypes.get(tmppnt.x).get(tmppnt.y).get(tmppnt.z).get(j).equals("Checker")
						|| replaytypes.get(tmppnt.x).get(tmppnt.y).get(tmppnt.z).get(j).equals("King"))
						&& (replayowners.get(tmppnt.x).get(tmppnt.y).get(tmppnt.z).get(j).equals(new Integer(moveowner)))){
						action = "Remove," + tmppnt.x + "," + tmppnt.y + "," + tmppnt.z + "," + j;
						System.out.println("found exploded piece to remove, action: " + action);
						this.replayactions.add(action);
					}
				}
				*/
			}
			//I think we have parsed this move at this point
			
		System.out.println("done building actions for turn: " + this.replaymovepos + ", move: " + this.replaymov);
		this.replayactpos = -1;
		this.nextAction();
	}
	
	//hidden controller method used to setup the information for the next action to be animated
	// this will parse the stored action to determine the desired effect
	private void nextAction() {
		
		//stop replays while we calculate stuff
		this.boardtimer.stop();
		
		//define common variables
		String[] splits = null;
		String actiontitle = "";
		Dimension tmpdim = null;
		Point tmppnt = null;
		int tmpboard = 0;
		Point tmppnt2 = null;
		int tmpboard2 = 0;
		int tmpindex = 0;
		String tmppiece = "";
		
		++this.replayactpos;
		System.out.println("setting up the next action: " + this.replayactpos);
		//verify we have another action available
		if (this.replayactions.size() > this.replayactpos){
			System.out.println("setting up: " + this.replayactions.get(replayactpos));
			//parse actions
			splits = this.replayactions.get(replayactpos).split("\\,");
			actiontitle = splits[0];
			if (actiontitle.equals("Add")){
				System.out.println("setting up add action");
				//Add action format: "ActionName,PieceType,x,y,z"
				this.animdims = new ArrayList<Dimension>();
				this.animpos = new ArrayList<Point>();
				tmppnt = new Point(0,0);
				tmpdim = new Dimension();
				tmpboard = 0;
				tmppiece = splits[1];
				try{
					tmppnt.x = Integer.parseInt(splits[2]);
					tmppnt.y = Integer.parseInt(splits[3]);
					tmpboard = Integer.parseInt(splits[4]);
				}catch (NumberFormatException EXC){}
				//simply add the piece to the replay board
				this.replaytypes.get(tmppnt.x).get(tmppnt.y).get(tmpboard).add(tmppiece);
				this.replayowners.get(tmppnt.x).get(tmppnt.y).get(tmpboard).add(new Integer(this.replayowner));
				this.animationstep = 0;
				this.animsteps = 1;
				this.animindex = -1;
			}else if (actiontitle.equals("Remove")){
				System.out.println("setting up remove action");
				//Remove action format: "ActionName,x,y,z,index"
				this.animdims = new ArrayList<Dimension>();
				this.animpos = new ArrayList<Point>();
				tmppnt = new Point(0,0);
				tmpdim = new Dimension();
				tmpboard = 0;
				tmpindex = 0;
				try{
					tmppnt.x = Integer.parseInt(splits[1]);
					tmppnt.y = Integer.parseInt(splits[2]);
					tmpboard = Integer.parseInt(splits[3]);
					tmpindex = Integer.parseInt(splits[4]);
				}catch (NumberFormatException EXC){}
				//simply remove the piece from the replay board
				this.replaytypes.get(tmppnt.x).get(tmppnt.y).get(tmpboard).remove(tmpindex);
				this.replayowners.get(tmppnt.x).get(tmppnt.y).get(tmpboard).remove(tmpindex);
				this.animationstep = 0;
				this.animsteps = 1;
				this.animindex = -1;
			}else if (actiontitle.equals("Move")){
				System.out.println("setting up move action");
				//Move action format: "ActionName,PieceType,start-x,start-y,start-z,end-x,end-y,end-z"
				this.animdims = new ArrayList<Dimension>();
				this.animpos = new ArrayList<Point>();
				tmppnt = new Point(0,0);
				tmppnt2 = new Point(0,0);
				tmpdim = new Dimension();
				tmpboard = 0;
				tmpboard2 = 0;
				tmppiece = splits[1];
				try{
					tmppnt.x = Integer.parseInt(splits[2]);
					tmppnt.y = Integer.parseInt(splits[3]);
					tmpboard = Integer.parseInt(splits[4]);
					tmppnt2.x = Integer.parseInt(splits[5]);
					tmppnt2.y = Integer.parseInt(splits[6]);
					tmpboard2 = Integer.parseInt(splits[7]);
				}catch (NumberFormatException EXC){}
				//define the positions and dimension changes
				this.animpos.add(this.getPixelFromLocation(new GameBoard.Point3D(tmppnt.x, tmppnt.y, tmpboard)));
				this.animpos.add(this.getPixelFromLocation(new GameBoard.Point3D(tmppnt2.x, tmppnt2.y, tmpboard2)));
				tmpindex = this.imagenames.indexOf(tmppiece);
				tmpdim = this.getSquareSize();
				this.animdims.add(tmpdim);
				this.animdims.add(new Dimension(tmpdim.width * 2, tmpdim.height * 2));
				this.animdims.add(tmpdim);
				this.animationstep = 0;
				this.animsteps = 25;
				this.animindex = tmpindex;
			}else if (actiontitle.equals("Destroy")){
				System.out.println("setting up destroy action");
				//Destroy action format: "ActionName,x,y,z"
				this.animdims = new ArrayList<Dimension>();
				this.animpos = new ArrayList<Point>();
				tmppnt = new Point(0,0);
				tmpdim = new Dimension();
				tmpboard = 0;
				try{
					tmppnt.x = Integer.parseInt(splits[1]);
					tmppnt.y = Integer.parseInt(splits[2]);
					tmpboard = Integer.parseInt(splits[3]);
				}catch (NumberFormatException EXC){}
				//define the positions and dimension changes
				this.animpos.add(this.getPixelFromLocation(new GameBoard.Point3D(tmppnt.x, tmppnt.y, tmpboard)));
				tmpindex = this.imagenames.indexOf("Destroy");
				tmpdim = this.getSquareSize();
				this.animdims.add(tmpdim);
				this.animationstep = 0;
				this.animsteps = 10;
				this.animindex = tmpindex;
			}else if (actiontitle.equals("Explode")){
				System.out.println("setting up explode action");
				//Explode action format: "ActionName,x,y,z"
				this.animdims = new ArrayList<Dimension>();
				this.animpos = new ArrayList<Point>();
				tmppnt = new Point(0,0);
				tmpdim = new Dimension();
				tmpboard = 0;
				try{
					tmppnt.x = Integer.parseInt(splits[1]);
					tmppnt.y = Integer.parseInt(splits[2]);
					tmpboard = Integer.parseInt(splits[3]);
				}catch (NumberFormatException EXC){}
				//define the positions and dimension changes
				this.animpos.add(this.getPixelFromLocation(new GameBoard.Point3D(tmppnt.x, tmppnt.y, tmpboard)));
				tmpindex = this.imagenames.indexOf("Explosion");
				tmpdim = this.getSquareSize();
				this.animdims.add(new Dimension(tmpdim.width * 3, tmpdim.height * 3));
				this.animationstep = 0;
				this.animsteps = 18;
				this.animindex = tmpindex;
			}
			System.out.println("done setting up this action");
		}else{
			System.out.println("we have no more actions to setup for this move, go build the actions for the next move or turn!");
			this.buildActions();
		}
		
		//start replays back up since we are done calculating
		//if we are actually in the middle of replays and not simply starting up
		if (this.getBoardState() == GameBoard.REPLAY) {
			this.boardtimer.start();
		}
		
	}
	
	//hidden convenience method for asking the user to input info in a text field
	private String requestInfo(String message, String title) {
		
		return JOptionPane.showInputDialog(this, 
			message, title, JOptionPane.QUESTION_MESSAGE);
		
	}
	
	//hidden convenience method for asking the user a yes/no question
	private boolean askYNQuestion(String question, String title) {
		
		int response = JOptionPane.showConfirmDialog(this, 
			question, title, JOptionPane.YES_NO_OPTION,
			JOptionPane.QUESTION_MESSAGE);
		
		if (response == JOptionPane.YES_OPTION){
			return true;
		}else{
			return false;
		}
	}
	
	//hidden convenience method for asking the user a yes/no/cancel question
	private int askYNCQuestion(String question, String title) {
		
		int response = JOptionPane.showConfirmDialog(this, 
			question, title, JOptionPane.YES_NO_CANCEL_OPTION,
			JOptionPane.QUESTION_MESSAGE);
		
		if (response == JOptionPane.YES_OPTION){
			return 0;
		}else if (response == JOptionPane.NO_OPTION){
			return 1;
		}else{
			return 2;
		}
	}
	
	//hidden convenience method for showing the user an information notice
	private void showInfoNotice(String info, String title) {
		this.repaint();
		((GameView)UIController.grabState("GameView")).gamebuttons.repaint();
		((GameView)UIController.grabState("GameView")).gamemenu.repaint();
		((GameView)UIController.grabState("GameView")).gamebuttons.revalidate();
		((GameView)UIController.grabState("GameView")).gamemenu.revalidate();
		JOptionPane.showMessageDialog(this, info, title, JOptionPane.INFORMATION_MESSAGE);
		
	}
	
	//hidden convenience method for showing the user the setup panels
	// and setting the parameters based on user choices
	private void requestSetup() {
		
		//this is what I want to use, but it is not working yet
		/*
		BoardSetup questionaire = new BoardSetup();
		String[] options = { "Apply" };
		JOptionPane.showMessageDialog(this, questionaire, "Board Setup",
			JOptionPane.QUESTION_MESSAGE);
		this.boardsize = questionaire.boardsize;
		this.coinside = questionaire.coinside;
		this.randomize = questionaire.randomize;
		*/
		//so I will use this instead
		///*
		//get board size
		String[] options = new String[2];
		options[0] = "8x8 [10]";
		options[1] = "10x10 [15]";
		Object result = null;
		while (result == null){
			result = JOptionPane.showInputDialog(this, "Select a board size:", "Board Setup - Board Size",
				JOptionPane.QUESTION_MESSAGE, null, options, options[0]);
		}
		if (result.equals(options[1])){
			this.boardsize = new Dimension(10,10);
		}else{
			this.boardsize = new Dimension(8,8);
		}
		//get coin side
		options = new String[2];
		options[0] = "Heads";
		options[1] = "Tails";
		result = null;
		while (result == null){
			result = JOptionPane.showInputDialog(this, this.playerlist.get(this.currentplayer) 
				+ ", select a coin side for the coin toss \n that will decide who goes first:", "Board Setup - Coin Side",
				JOptionPane.QUESTION_MESSAGE, null, options, options[0]);
		}
		if (result.equals(options[1])){
			this.coinside = 1;
		}else{
			this.coinside = 0;
		}
		//get randomization
		options = new String[2];
		options[0] = "No";
		options[1] = "Yes";
		result = null;
		while (result == null){
			result = JOptionPane.showInputDialog(this, "Randomize placement of all pieces:", "Board Setup - Randomize",
				JOptionPane.QUESTION_MESSAGE, null, options, options[0]);
		}
		if (result.equals(options[1])){
			this.randomize = true;
		}else{
			this.randomize = false;
		}
		//*/
		
	}
	
	//hidden convenience method for performing game setup tasks
		// asking the user to answer some questions
		// after the questions are answered, we can setup some of the game data properly
		// then we can change our mode to PLACEMENT
	private void setupGame() {
		
		//ask setup questions
		this.requestSetup();
		//this.boardsize = new Dimension(8,8);
		//this.coinside = 0;
		//this.randomize = false;
		//perform a coin toss
		if (UIController.coinToss(this.coinside)){
			this.firstplayer = 0;
		}else{
			this.firstplayer = 1;
		}
		//tell who won the coin toss
		//this.showInfoNotice(this.playerlist.get(this.firstplayer) + ", you have won the coin toss!", "Coin Toss Winner");
		UIController.printPopup(this.playerlist.get(this.firstplayer) + " won the coin toss!", 8.5);
		//set our current player
		this.currentplayer = this.firstplayer;
		//setup data
		this.boardcount = 2;
		UIController.addBoards(this.boardcount, this.boardsize);
		this.playercount = 2;
		this.defineBoardStructure();
		//perform randomization if necessary
		if (this.randomize){
			//tell that we are going into game play
			/*
			this.showInfoNotice("It is time to play the game..."
				+ "\n" + this.playerlist.get(this.firstplayer) + ", you will make the first move!", "Game Play Started");
				*/
			UIController.printPopup("Game play started..."
				+"<br>" + this.playerlist.get(this.firstplayer) + ", you will move first!", 8.5);
			UIController.randomizePlacement();
			//update our boards to match the randomized boards
			this.syncBoardData();
		}else{
			//tell that we are going into game play
			/*
			this.showInfoNotice("It is time to place pieces..."
				+ "\n" + this.playerlist.get(this.firstplayer) + ", you will place the first piece!", "Piece Placement Started");
				*/
			UIController.printPopup("Piece placement started..."
				+ "<br>" + this.playerlist.get(this.firstplayer) + " will place the first piece!", 9.0);
			//if we dont randomize, we should do placement
			this.setBoardState(GameBoard.PLACEMENT);
		}
		
	}
	
	
	
	
	
	//internal class Point3D used for convenience
	private class Point3D extends Point {
		
		//declare instance vars
		public int z;
		
		//default constructor
		public Point3D() {
			//call ancestor
			super();
			z = 0;
		}
		//custom constructor
		public Point3D(int newx, int newy, int newz) {
			//call ancestor
			super(newx,newy);
			z = newz;
		}
		//custom constructor
		public Point3D(Point3D newpnt) {
			//call ancestor
			super(newpnt.x,newpnt.y);
			z = newpnt.z;
		}
		
		//override ancestor's methods
		public void setLocation(double newx, double newy, double newz) {
			//call ancestor
			super.setLocation(newx,newy);
			z = (int)newz;
		}
		public void setLocation(int newx, int newy, int newz) {
			//call ancestor
			super.setLocation(newx,newy);
			z = newz;
		}
		public void translate(int dx, int dy, int dz) {
			//call ancestor
			super.translate(dx,dy);
			z += dz;
		}
		public boolean equals(GameBoard.Point3D otherpnt) {
			if (otherpnt.x == this.x && otherpnt.y == this.y && otherpnt.z == this.z){
				return true;
			}else{
				return false;
			}
		}
		public String toString() {
			return ("[x=" + super.x + ",y=" + super.y + ",z=" + this.z + "]");
		}
		
		//add a new method
		public double getZ() {
			return (double)z;
		}
		
	}
	
	
	//internal JPanel extension class used for convenience
	// structures a JPanel used to display game board setup options
	private class BoardSetup extends JPanel implements ActionListener {
		
		//vars we need
		private JComboBox boardchoice;
		private JComboBox coinchoice;
		private JComboBox randomchoice;
		public Dimension boardsize;
		public int coinside;
		public boolean randomize;
		
		//constructor
		public BoardSetup() {
			
			//call ancestor
			super();
			//setup layout
			setLayout(new GridLayout(1,3));
			//setup combo boxes
			//board choice
			boardchoice = new JComboBox();
			boardchoice.addItem("8x8 (10)");
			boardchoice.addItem("10x10 (15)");
			boardchoice.setSelectedIndex(0);
			boardsize = new Dimension(8,8);
			boardchoice.add(this);
			JPanel paneltemp1 = new JPanel();
			paneltemp1.setLayout(new FlowLayout());
			JLabel labeltemp1 = new JLabel();
			labeltemp1.setText("Select a board size:");
			paneltemp1.add(labeltemp1);
			paneltemp1.add(boardchoice);
			this.add(paneltemp1);
			//coin choice
			coinchoice = new JComboBox();
			coinchoice.addItem("Heads");
			coinchoice.addItem("Tails");
			coinchoice.setSelectedIndex(0);
			coinside = 0;
			coinchoice.add(this);
			JPanel paneltemp2 = new JPanel();
			paneltemp2.setLayout(new FlowLayout());
			JLabel labeltemp2 = new JLabel();
			labeltemp2.setText("Select a coin side:");
			paneltemp2.add(labeltemp2);
			paneltemp2.add(coinchoice);
			add(paneltemp2);
			//random choice
			randomchoice = new JComboBox();
			randomchoice.addItem("No");
			randomchoice.addItem("Yes");
			randomchoice.setSelectedIndex(0);
			randomize = false;
			randomchoice.add(this);
			JPanel paneltemp3 = new JPanel();
			paneltemp3.setLayout(new FlowLayout());
			JLabel labeltemp3 = new JLabel();
			labeltemp3.setText("Randomize piece placement:");
			paneltemp3.add(labeltemp3);
			paneltemp3.add(randomchoice);
			add(paneltemp3);
			//set visibilities
			setVisible(true);
			setOpaque(true);
			
		}
		
		//overridden for ActionListener
		public void actionPerformed(ActionEvent EVE) {
			
			//check who was selected
			Object tmpobj = EVE.getSource();
			if (tmpobj.equals(boardchoice)){
				if (boardchoice.getSelectedIndex() == 0){
					boardsize.width = 8;
					boardsize.height = 8;
				}else{
					boardchoice.setSelectedIndex(1);
					boardsize.width = 10;
					boardsize.height = 10;
				}
			}else if (tmpobj.equals(coinchoice)){
				if (coinchoice.getSelectedIndex() == 0){
					coinside = 0;
				}else{
					coinchoice.setSelectedIndex(1);
					coinside = 1;
				}
			}else if (tmpobj.equals(randomchoice)){
				if (randomchoice.getSelectedIndex() == 0){
					randomize = false;
				}else{
					randomchoice.setSelectedIndex(1);
					randomize = true;;
				}
			}
			
		}
		
	}
	
	
	
}